Subversion Repositories HomeAutomation

Rev

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

  1.  
  2. function CanNode(hardwareId, numberOfModules)
  3. {
  4.     this.myHardwareId = hardwareId;
  5.     this.myNumberOfModules = numberOfModules;
  6.     this.myModules = new Array();
  7.     this.myHeartbeatTime = 0;
  8.     this.myIsOnline = false;
  9.     this.stopProgramming();
  10. }
  11.  
  12. CanNode.prototype.myIsOnline = null;
  13. CanNode.prototype.myHardwareId = null;
  14. CanNode.prototype.myNumberOfModules = null;
  15. CanNode.prototype.myModules = null;
  16. CanNode.prototype.myHeartbeatTime = null;
  17. CanNode.prototype.myProgrammingIsBios = null;
  18. CanNode.prototype.myProgrammingBiosOffset = null;
  19. CanNode.prototype.myProgramming = null;
  20. CanNode.prototype.myProgrammingHex = null;
  21. CanNode.prototype.myProgrammingHexAddress = null;
  22. CanNode.prototype.myProgrammingOffset = null;
  23. CanNode.prototype.myProgrammingCallback = null;
  24. CanNode.prototype.myProgrammingWantAck = null;
  25. CanNode.prototype.myProgrammingState = null;
  26. CanNode.prototype.myProgrammingLastPercent = null;
  27. CanNode.prototype.myProgrammingLastTenPercent = null;
  28. CanNode.prototype.myProgrammingnrOfTicks = null;
  29. CanNode.prototype.myProgrammingTimeout = null;
  30. CanNode.prototype.myProgrammingResend = null;
  31. CanNode.prototype.myProgrammingLastPacket = null;
  32. CanNode.prototype.myProgrammingTimeStarted = null;
  33. CanNode.prototype.myResetCallback = null;
  34. CanNode.prototype.myResetTimer = null;
  35.  
  36. CanNode.prototype.isOnline = function()
  37. {
  38.     return this.myIsOnline;
  39. }
  40.  
  41. CanNode.prototype.start = function()
  42. {
  43.     var canMessage = new CanNMTMessage("nmt", "Start_App");
  44.     canMessage.setData("HardwareId", this.myHardwareId);
  45.     canMessage.send();
  46. }
  47.  
  48. CanNode.prototype.reset = function(callback)
  49. {
  50.     var self = this;
  51.    
  52.     if (callback)
  53.     {
  54.         this.myResetCallback = callback;
  55.         if (this.myResetTimer)
  56.         {
  57.             this.myResetTimer.stop();
  58.         }
  59.        
  60.         this.myResetTimer = new Interval(   function()
  61.                             {
  62.                                 self.myResetTimer.stop();
  63.                                 self.myResetTimer = null;
  64.                                 self.myResetCallback(false);
  65.                                 self.myResetCallback = null;
  66.                             }, 5000);
  67.         this.myResetTimer.start();
  68.     }
  69.  
  70.     var canMessage = new CanNMTMessage("nmt", "Reset");
  71.     canMessage.setData("HardwareId", this.myHardwareId);
  72.     canMessage.send();
  73.    
  74.     this.setOffline();
  75. }
  76.  
  77. CanNode.prototype.startApplication = function()
  78. {
  79.     var canMessage = new CanNMTMessage("nmt", "Start_App");
  80.     canMessage.setData("HardwareId", this.myHardwareId);
  81.     canMessage.send();
  82. }
  83.  
  84. CanNode.prototype.handleHeartbeat = function(numberOfModules)
  85. {
  86.     if (numberOfModules)
  87.     {
  88.         this.myNumberOfModules = numberOfModules;
  89.     }
  90.    
  91.     if (this.myNumberOfModules == null || this.myNumberOfModules > this.myModules.length)
  92.     {
  93.         var canMessage = new CanMessage("mnmt", "To_Owner", "", 0, "List");
  94.         canMessage.setData("HardwareId", this.myHardwareId);
  95.         canMessage.send();
  96.     }
  97.    
  98.     this.onlineHandler();
  99. }
  100.  
  101. CanNode.prototype.startProgramming = function(hexData, progressCallback, isBios)
  102. {
  103.     this.myProgrammingCallback = progressCallback;
  104.     if (isBios)
  105.     {
  106.         this.myProgrammingIsBios = isBios;
  107.         this.myProgrammingBiosOffset = hexData.getAddrLower();
  108.         //this.myProgrammingCallback(true, "", "got IsBios, address: " + this.myProgrammingBiosOffset, false);
  109.     }
  110.     else
  111.     {
  112.         this.myProgrammingIsBios = false;
  113.     }
  114.    
  115.     this.myProgramming = true;
  116.     this.myProgrammingHex = hexData;
  117.    
  118.     CanProgrammingNode = this;
  119.    
  120.     var self = this;
  121.     this.myProgrammingTimeout = new Interval(function() { self.programmingTimeout() }, 6000);
  122.     this.myProgrammingTimeout.start();
  123.     this.myProgrammingState = "RESET";
  124.  
  125.     this.reset();
  126. }
  127.  
  128. CanNode.prototype.programmingTimeout = function()
  129. {
  130.     switch (this.myProgrammingState)
  131.     {
  132.     case "RESET":
  133.         this.stopProgramming(true, "Timeout while waiting for node to reset");
  134.         this.reset();
  135.         break;
  136.     case "DATA":
  137.         this.stopProgramming(true, "Timeout while waiting for node to ack data packet");
  138.         this.reset();
  139.         break;
  140.     case "END":
  141.         this.stopProgramming(true, "Timeout while waiting for node to ack data packet*");
  142.         this.reset();
  143.         break;
  144.     case "CRC":
  145.         this.stopProgramming(true, "Timeout while waiting for node to send crc");
  146.         this.reset();
  147.         break;
  148.     case "COPY":
  149.         //this will never occur, bios does not send ack for copy command, it just moves data in flash then reset itself
  150.         this.myProgrammingCallback(true, "ERROR", "Unexpected error 100", false);
  151.         break;
  152.     default:
  153.         this.myProgrammingCallback(true, "ERROR", "Unexpected error 101, state: " + this.myProgrammingState, false);
  154.         break;
  155.     }
  156. }
  157.  
  158. CanNode.prototype.programmingResend = function()
  159. {
  160. this.myProgrammingCallback(true, "", "Resend timeout, a packet was lost, either program data or ack", false);
  161.     this.myProgrammingResend.reset();
  162.     //if we did not got ack then try to resend the data
  163.     this.myProgrammingLastPacket.send();
  164. }
  165.  
  166. CanNode.prototype.handleBiosStart = function(biosVersion, hasApplication)
  167. {
  168.     if (this.myResetCallback)
  169.     {
  170.         this.myResetCallback(true);
  171.         this.myResetCallback = null;
  172.     }
  173.    
  174.     if (this.myResetTimer)
  175.     {
  176.         this.myResetTimer.stop();
  177.         this.myResetTimer = null;
  178.     }
  179.  
  180. //print("node was reset and bios just started, ver:" + biosVersion);
  181.     if (this.myProgramming)
  182.     {
  183.         switch (this.myProgrammingState)
  184.         {
  185.         case "RESET":
  186.             this.myProgrammingHexAddress = this.myProgrammingHex.getAddrLower();
  187.             this.myProgrammingWantAck = (((this.myProgrammingHexAddress-this.myProgrammingBiosOffset)<<8)|((this.myProgrammingHexAddress-this.myProgrammingBiosOffset)>>8))&0xffff;
  188.  
  189.             var canMessage = new CanNMTMessage("nmt", "Pgm_Start");
  190.             canMessage.setData("HardwareId", this.myHardwareId);
  191.             canMessage.setData("Address0", (this.myProgrammingHexAddress-this.myProgrammingBiosOffset)&0xff);
  192.             canMessage.setData("Address1", ((this.myProgrammingHexAddress-this.myProgrammingBiosOffset)>>8)&0xff);
  193.             canMessage.setData("Address3", ((this.myProgrammingHexAddress-this.myProgrammingBiosOffset)>>16)&0xff);
  194.             canMessage.setData("Address4", ((this.myProgrammingHexAddress-this.myProgrammingBiosOffset)>>24)&0xff);
  195.             canMessage.send();
  196.             this.myProgrammingState = "DATA";
  197.        
  198.             var self = this;
  199.             this.myProgrammingTimeout.setTimeout(1000);
  200.        
  201.             this.myProgrammingResend = new Interval(function() { self.programmingResend() }, 1100);
  202.             this.myProgrammingResend.start();
  203.  
  204.             var date = new Date();
  205.             this.myProgrammingTimeStarted = date.getTime();
  206.  
  207.             this.myProgrammingCallback(true, "START", "Started programming of node", false);
  208.             this.myProgrammingCallback(true, "", "  0% [", true);
  209.             break;
  210.         case "COPY":
  211.             //bios was moved and now started (so it seems like it was successfully programmed and moved)
  212.             //now we should send a new programming with one empty packet to erase application area
  213.             this.myProgrammingWantAck = 0;
  214.             var canMessage = new CanNMTMessage("nmt", "Pgm_Start");
  215.             canMessage.setData("HardwareId", this.myHardwareId);
  216.             canMessage.setData("Address0", 0);
  217.             canMessage.setData("Address1", 0);
  218.             canMessage.setData("Address3", 0);
  219.             canMessage.setData("Address4", 0);
  220.             canMessage.send();
  221.             this.myProgrammingState = "APPCLEANDATA";
  222.             break;
  223.         case "DONE":
  224.             this.stopProgramming(true, "Done, successful");
  225.             break;
  226.         default:
  227.             this.myProgrammingCallback(true, "ERROR", "Unexpected error 105, state: " + this.myProgrammingState, false);
  228.             break;
  229.         }
  230.    
  231.     }
  232. }
  233.  
  234. CanNode.prototype.handleNack = function(data)
  235. {
  236.     this.stopProgramming(true, "Failed, got NACK, sending reset");
  237.     this.reset();
  238.     if (false) 
  239.     {
  240.     }
  241. }
  242.  
  243. CanNode.prototype.handleAck = function(data)
  244. {
  245.     this.myProgrammingTimeout.reset();
  246.  
  247.     if (data == this.myProgrammingWantAck) 
  248.     {
  249.         switch (this.myProgrammingState)
  250.         {
  251.         case "DATA":
  252.             var offset = this.myProgrammingHexOffset;
  253.             this.myProgrammingResend.setTimeout(100);
  254.             this.myProgrammingResend.reset();
  255.        
  256.             this.myProgrammingWantAck = ((offset<<8)|(offset>>8))&0xffff;
  257.             var bytesLeft = this.myProgrammingHex.getLength() - this.myProgrammingHexOffset;
  258.            
  259.             //here we must handle the possibility that data is less then 48 bits
  260.             var canMessage = new CanNMTMessage("nmt", "Pgm_Data_48");
  261.             switch (bytesLeft)
  262.             {
  263.             case 1:
  264.                 canMessage = new CanNMTMessage("nmt", "Pgm_Data_8");
  265.                 break;
  266.             case 2:
  267.                 canMessage = new CanNMTMessage("nmt", "Pgm_Data_16");
  268.                 break;
  269.             case 3:
  270.                 canMessage = new CanNMTMessage("nmt", "Pgm_Data_24");
  271.                 break;
  272.             case 4:
  273.                 canMessage = new CanNMTMessage("nmt", "Pgm_Data_32");
  274.                 break;
  275.             case 5:
  276.                 canMessage = new CanNMTMessage("nmt", "Pgm_Data_40");
  277.                 break;
  278.             }
  279.            
  280.             canMessage.setData("Offset0", offset&0xff);
  281.             canMessage.setData("Offset1", (offset>>8)&0xff);
  282.             for (var i=0; i<bytesLeft && i<6; i++)
  283.             {
  284.                 switch (i)
  285.                 {
  286.                 case 0:
  287.                     canMessage.setData("Data0", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  288.                     break;
  289.                 case 1:
  290.                     canMessage.setData("Data1", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  291.                     break;
  292.                 case 2:
  293.                     canMessage.setData("Data2", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  294.                     break;
  295.                 case 3:
  296.                     canMessage.setData("Data3", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  297.                     break;
  298.                 case 4:
  299.                     canMessage.setData("Data4", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  300.                     break;
  301.                 case 5:
  302.                     canMessage.setData("Data5", this.myProgrammingHex.getByte(this.myProgrammingHexAddress+offset));
  303.                     break;
  304.                 }
  305.                 offset++;
  306.             }
  307.             canMessage.send();
  308.             this.myProgrammingHexOffset = offset;
  309.  
  310.             if (this.myProgrammingHexOffset >= this.myProgrammingHex.getLength()) {
  311.                 this.myProgrammingState = "END";
  312.             }
  313.            
  314.             //this.myProgrammingCallback(true, "PROGRESS", this.myProgrammingHexOffset + ":" + this.myProgrammingHex.getLength(), false);
  315.             var percent = 100*this.myProgrammingHexOffset / this.myProgrammingHex.getLength();
  316.             if (percent >= this.myProgrammingLastPercent+2.5)
  317.             {
  318.                 this.myProgrammingCallback(true, "", "=", true);
  319.                 this.myProgrammingLastPercent+=2.5;
  320.                 this.myProgrammingnrOfTicks++;
  321.             }
  322.             if (percent > this.myProgrammingLastTenPercent+10)
  323.             {
  324.                 for (var i=this.myProgrammingnrOfTicks; i<40; i++) this.myProgrammingCallback(true, "", " ", true);
  325.                 this.myProgrammingCallback(true, "", "]", true);
  326.                 this.myProgrammingCallback(true, "", "\r", true);
  327.                 this.myProgrammingCallback(true, "", " "+Math.round(percent)+"% [", true);
  328.                 this.myProgrammingLastTenPercent+=10;
  329.                 for (var i=0; i<this.myProgrammingnrOfTicks; i++) this.myProgrammingCallback(true, "", "=", true);
  330.             }
  331.            
  332.             this.myProgrammingLastPacket = canMessage;
  333.             break;
  334.        
  335.         case "END":
  336.             this.myProgrammingWantAck = ((this.myProgrammingHex.getCRC16()<<8)|(this.myProgrammingHex.getCRC16()>>8))&0xffff;
  337.            
  338.             var timeElapsed = 0;
  339.             var printTime = "";
  340.             if (this.myProgrammingTimeStarted>0)
  341.             {
  342.                 var date = new Date();
  343.                 timeElapsed = date.getTime() - this.myProgrammingTimeStarted;
  344.                 printTime = " in " + Math.round(timeElapsed/1000) + "s, " + Math.round(this.myProgrammingHex.getLength()*1000/timeElapsed)+"bytes/s";
  345.             }
  346.            
  347.             var canMessage = new CanNMTMessage("nmt", "Pgm_End");
  348.             canMessage.send();
  349.  
  350.             this.myProgrammingState = "CRC";
  351.             for (var i=this.myProgrammingnrOfTicks; i<40; i++) this.myProgrammingCallback(true, "", " ", true);
  352.             this.myProgrammingCallback(true, "", "]", true);
  353.             this.myProgrammingCallback(true, "", "\r", true);
  354.             this.myProgrammingCallback(true, "", "100% [========================================]"+printTime, true);
  355.  
  356.             if (this.myProgrammingResend != null)
  357.             {
  358.                 /* Stop the interval */
  359.                 this.myProgrammingResend.stop();
  360.             }
  361.  
  362.             break;
  363.            
  364.         case "CRC":
  365.             //is this a bios programming?
  366.             if (this.myProgrammingIsBios)
  367.             {
  368.                 this.myProgrammingTimeout.setTimeout(6000);
  369.                 this.myProgrammingState = "COPY";
  370.                 this.myProgrammingCallback(true, "\nBIOS", "Start to copy bios to new location", false);
  371.                 var canMessage = new CanNMTMessage("nmt", "Pgm_Copy");
  372.                 canMessage.setData("Source0", 0);
  373.                 canMessage.setData("Source1", 0);
  374.                 canMessage.setData("Destination0", this.myProgrammingBiosOffset&0xff);
  375.                 canMessage.setData("Destination1", (this.myProgrammingBiosOffset>>8)&0xff);
  376.                 canMessage.setData("Length0", (this.myProgrammingHex.getLength())&0xff);
  377.                 canMessage.setData("Length1", (this.myProgrammingHex.getLength()>>8)&0xff);
  378.                 canMessage.send();
  379.                 //this.stopProgramming();
  380.             }
  381.             else
  382.             {
  383.                 this.myProgrammingState = "DONE";
  384.                 this.reset();
  385.             }
  386.             break;
  387.        
  388.         case "COPY":    //this will never occur, bios does not send ack for copy command, it just moves data in flash then reset itself
  389.             break;
  390.         case "APPCLEANDATA":
  391.             var canMessage = new CanNMTMessage("nmt", "Pgm_Data_16");
  392.             this.myProgrammingWantAck = 0;
  393.             this.myProgrammingState = "APPCLEANEND";
  394.             canMessage.setData("Data0", 0xff);
  395.             canMessage.setData("Data1", 0xff);
  396.             canMessage.send();
  397.             break;
  398.         case "APPCLEANEND":
  399.             this.myProgrammingWantAck = 0x1b0;
  400.             this.myProgrammingState = "APPCLEANCRC";
  401.             var canMessage = new CanNMTMessage("nmt", "Pgm_End");
  402.             canMessage.send();
  403.             break;
  404.         case "APPCLEANCRC":
  405.             this.myProgrammingState = "DONE";
  406.             this.reset();
  407.             break;
  408.        
  409.         default:
  410.             this.myProgrammingCallback(true, "ERROR", "Unexpected error 104, state: " + this.myProgrammingState, false);
  411.             break;
  412.         }
  413.     }
  414.     else
  415.     {
  416.         switch (this.myProgrammingState)
  417.         {
  418.         case "DATA":
  419.             //Fall through
  420.         case "END":
  421.             ///TODO: We got something that was not correct... we should abort
  422.             this.stopProgramming(true, "Failed to program, ack had wrong offset, expected data: "+this.myProgrammingWantAck+" got: "+data);
  423.             this.reset();
  424.             break;
  425.         case "CRC":
  426.             this.stopProgramming(true, "Failed to program, CRC not matching, expected data: "+this.myProgrammingWantAck+" got: "+data);
  427.             this.reset();
  428.             break;
  429.         case "COPY":    //this will never occur, bios does not send ack for copy command, it just moves data in flash then reset itself
  430.             this.myProgrammingCallback(true, "ERROR", "Unexpected error 103", false);
  431.             break;
  432.         default:
  433.             this.myProgrammingCallback(true, "ERROR", "Unexpected error 102, state: " + this.myProgrammingState, false);
  434.             break;
  435.         }
  436.     }
  437. }
  438.  
  439. CanNode.prototype.stopProgramming = function(status, text)
  440. {
  441.     if (status && text)
  442.     {
  443.         this.myProgrammingCallback(status, "\nSTOP", text, false);
  444.     }
  445.  
  446.     this.myProgrammingIsBios = false;
  447.     this.myProgramming = false;
  448.     this.myProgrammingHex = new Array();
  449.     this.myProgrammingHexAddress = 0;
  450.     this.myProgrammingHexOffset = 0;
  451.     this.myProgrammingCallback = function() {};
  452.     this.myProgrammingWantAck = null;
  453.     this.myProgrammingState = "";
  454.     this.myProgrammingLastPercent = 0;
  455.     this.myProgrammingLastTenPercent = 0;
  456.     this.myProgrammingnrOfTicks = 0;
  457.     this.myProgrammingTimeStarted = 0;
  458.     this.myProgrammingBiosOffset = 0;
  459.  
  460.     if (this.myProgrammingTimeout != null)
  461.     {
  462.         /* Stop the interval */
  463.         this.myProgrammingTimeout.stop();
  464.     }
  465.     if (this.myProgrammingResend != null)
  466.     {
  467.         /* Stop the interval */
  468.         this.myProgrammingResend.stop();
  469.     }
  470.  
  471.     CanProgrammingNode = null;
  472. }
  473.  
  474.  
  475.  
  476. CanNode.prototype.addService = function(sequenceNumber, service)
  477. {
  478.     this.myModules[sequenceNumber] = service;
  479.     service.setOnline();
  480. }
  481.  
  482. CanNode.prototype.onlineHandler = function()
  483. {
  484.     var date = new Date();
  485.     this.myHeartbeatTime = date.getTimestamp();
  486.  
  487.     if (!this.myIsOnline)
  488.     {
  489.         this.myIsOnline = true;
  490.  
  491.         log(uint2hex(this.myHardwareId, 32) + "> Node went online\n");
  492.        
  493.         for (var n in this.myModules)
  494.         {
  495.             this.myModules[n].setOnline();
  496.         }
  497.     }
  498. }
  499.  
  500. CanNode.prototype.checkOffline = function()
  501. {
  502.     if (this.myIsOnline)
  503.     {
  504.         var date = new Date();
  505.         if (this.myHeartbeatTime + 10 < date.getTimestamp())
  506.         {
  507.             this.setOffline();
  508.         }
  509.     }
  510. }
  511.  
  512. CanNode.prototype.setOffline = function()
  513. {
  514.     if (this.myIsOnline)
  515.     {
  516.         this.myIsOnline = false;
  517.  
  518.         log(uint2hex(this.myHardwareId, 32) + "> Node went offline\n");
  519.        
  520.         for (var n in this.myModules)
  521.         {
  522.             this.myModules[n].setOffline();
  523.         }
  524.     }
  525. }
  526.