Advertisement
DanSt3fan

TCU_Code

May 30th, 2025 (edited)
375
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.49 KB | Source Code | 0 0
  1. /*@!Encoding:1252*/
  2. includes
  3. {
  4.  
  5. }
  6.  
  7. variables
  8. {
  9.   int crctable[256] = {0x00, 0x2F, 0x5E, 0x71, 0xBC, 0x93, 0xE2, 0xCD, 0x57, 0x78, 0x09, 0x26, 0xEB, 0xC4, 0xB5, 0x9A,
  10.                        0xAE, 0x81, 0xF0, 0xDF, 0x12, 0x3D, 0x4C, 0x63, 0xF9, 0xD6, 0xA7, 0x88, 0x45, 0x6A, 0x1B, 0x34,
  11.                        0x73, 0x5C, 0x2D, 0x02, 0xCF, 0xE0, 0x91, 0xBE, 0x24, 0x0B, 0x7A, 0x55, 0x98, 0xB7, 0xC6, 0xE9,
  12.                        0xDD, 0xF2, 0x83, 0xAC, 0x61, 0x4E, 0x3F, 0x10, 0x8A, 0xA5, 0xD4, 0xFB, 0x36, 0x19, 0x68, 0x47,
  13.                        0xE6, 0xC9, 0xB8, 0x97, 0x5A, 0x75, 0x04, 0x2B, 0xB1, 0x9E, 0xEF, 0xC0, 0x0D, 0x22, 0x53, 0x7C,
  14.                        0x48, 0x67, 0x16, 0x39, 0xF4, 0xDB, 0xAA, 0x85, 0x1F, 0x30, 0x41, 0x6E, 0xA3, 0x8C, 0xFD, 0xD2,
  15.                        0x95, 0xBA, 0xCB, 0xE4, 0x29, 0x06, 0x77, 0x58, 0xC2, 0xED, 0x9C, 0xB3, 0x7E, 0x51, 0x20, 0x0F,
  16.                        0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6, 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1,
  17.                        0xE3, 0xCC, 0xBD, 0x92, 0x5F, 0x70, 0x01, 0x2E, 0xB4, 0x9B, 0xEA, 0xC5, 0x08, 0x27, 0x56, 0x79,
  18.                        0x4D, 0x62, 0x13, 0x3C, 0xF1, 0xDE, 0xAF, 0x80, 0x1A, 0x35, 0x44, 0x6B, 0xA6, 0x89, 0xF8, 0xD7,
  19.                        0x90, 0xBF, 0xCE, 0xE1, 0x2C, 0x03, 0x72, 0x5D, 0xC7, 0xE8, 0x99, 0xB6, 0x7B, 0x54, 0x25, 0x0A,
  20.                        0x3E, 0x11, 0x60, 0x4F, 0x82, 0xAD, 0xDC, 0xF3, 0x69, 0x46, 0x37, 0x18, 0xD5, 0xFA, 0x8B, 0xA4,
  21.                        0x05, 0x2A, 0x5B, 0x74, 0xB9, 0x96, 0xE7, 0xC8, 0x52, 0x7D, 0x0C, 0x23, 0xEE, 0xC1, 0xB0, 0x9F,
  22.                        0xAB, 0x84, 0xF5, 0xDA, 0x17, 0x38, 0x49, 0x66, 0xFC, 0xD3, 0xA2, 0x8D, 0x40, 0x6F, 0x1E, 0x31,
  23.                        0x76, 0x59, 0x28, 0x07, 0xCA, 0xE5, 0x94, 0xBB, 0x21, 0x0E, 0x7F, 0x50, 0x9D, 0xB2, 0xC3, 0xEC,
  24.                        0xD8, 0xF7, 0x86, 0xA9, 0x64, 0x4B, 0x3A, 0x15, 0x8F, 0xA0, 0xD1, 0xFE, 0x33, 0x1C, 0x6D, 0x42}; // Look-up table used for E2E
  25.   int dataid[16] = {0x3B, 0x14, 0x65, 0x4A, 0x87, 0xA8, 0xD9, 0xF6, 0x6C, 0x43, 0x32, 0x1D, 0xD0, 0xFF, 0x8E, 0xA1}; // DataID List used for E2E
  26.   int e2e_123, e2e_124; // Used for E2E check of the Rx Messages
  27.   byte data_123[8], data_124[8]; // Used for E2E check of the Rx Messages
  28.   mstimer e2e_ET1, e2e_ET1_old, e2e_ET2, e2e_ET2_old;
  29.  
  30.   int condm, state;
  31.   float gearr[6] = {3.55, 3.73, 2.05, 1.32, 0.97, 0.65}; // Gearbox drive ratios - [R, 1st, 2nd, 3rd, 4th, 5th].
  32. }
  33.  
  34. on start // Timers used for E2E function calling
  35. {
  36.   setTimer(e2e_ET1, 150);
  37.   setTimer(e2e_ET2, 150);
  38. }
  39.  
  40. on timer e2e_ET1{ // Calling the E2E function for ET1 message
  41.   setTimer(e2e_ET1, 100);
  42.   if(isTimerActive(e2e_ET1_old) == 0){
  43.     sysSetVariableString(sysvar::sysNS::E2E_ET1_Status, "Timeout Error");
  44.   }
  45. }
  46.  
  47. on timer e2e_ET2{ // Calling the E2E function for ET2 message
  48.   setTimer(e2e_ET2, 100);
  49.   if(isTimerActive(e2e_ET2_old) == 0){
  50.     sysSetVariableString(sysvar::sysNS::E2E_ET2_Status, "Timeout Error");
  51.   }
  52. }
  53.  
  54. on timer e2e_ET1_old{
  55.  
  56. }
  57.  
  58. on timer e2e_ET2_old{
  59.  
  60. }
  61.  
  62. /// <Rx>
  63. on message Engine_Transmission_1
  64. {
  65.   int i; // where i is used for e2e checking and cond, condd are used for autoshifting in the autoshift function.
  66.   for(i = 0; i < this.dlc; i++)
  67.   {
  68.     data_123[i] = this.byte(i);
  69.   }
  70.   setTimer(e2e_ET1_old, 100);
  71.   e2e_123 = E2ECheck(this.id, this.dlc, data_123);
  72.   if(e2e_123){
  73.     if($sig_engine_PRNDM_Req == 0x0 || $sig_engine_PRNDM_Req == 0x2){ // check if vehicle in park or neutral to allow for manual shifting position
  74.       condm = 1;
  75.       state = 1; // we use to this to know if we need to engage 1st gear
  76.     }
  77.     if($sig_engine_PRNDM_Req == 0x1){
  78.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[0]; // calculate the rpm for reverse based on throttle input
  79.       if($Transmission_Engine_1::sig_transmission_RPM > 0 ){
  80.         condm = 0; // doesn't allow to go into manual shifting if reverse is engaged
  81.         state = 1;
  82.       }
  83.     }
  84.     if($sig_engine_PRNDM_Req == 0x3){
  85.       AutoShift();
  86.       condm = 1;
  87.       state = 2;
  88.     }
  89.     if($sig_engine_PRNDM_Req == 0x4 && condm == 1){
  90.       ManualShift();
  91.     }
  92.   }
  93. }
  94.  
  95.  
  96. /// <Rx>
  97. on message Engine_Transmission_2
  98. {
  99.   int i;
  100.   for(i = 0; i < this.dlc; i++)
  101.   {
  102.     data_124[i] = this.byte(i);
  103.   }
  104.   setTimer(e2e_ET2_old, 100);
  105.   e2e_124 = E2ECheck(this.id, this.dlc, data_124);
  106.   if(e2e_124){
  107.     if($sig_engine_PRNDM_Req == 0x0){ // If in park engage 1st gear to prevent rolling.
  108.       if($sig_transmission_RPM == 0){
  109.         $Transmission_Engine_1::sig_transmission_Gear1 = 1;
  110.         $Transmission_Engine_2::sig_transmission_Gear2 = 1;
  111.         $Transmission_Engine_2::sig_transmission_PRNDM_Conf = $sig_engine_PRNDM_Req;
  112.       }else{
  113.         $sig_transmission_PRNDM_Conf = 5;
  114.       }
  115.     }
  116.     if($sig_engine_PRNDM_Req == 0x1){ // If in reverse engage reverse gear
  117.       if($sig_transmission_RPM == 0){ // Fail safe in case we switch to reverse while in Drive
  118.         $Transmission_Engine_1::sig_transmission_Gear1 = 6;
  119.         $Transmission_Engine_2::sig_transmission_Gear2 = 6;
  120.         $Transmission_Engine_2::sig_transmission_PRNDM_Conf = $sig_engine_PRNDM_Req;
  121.       }else{
  122.         $Transmission_Engine_2::sig_transmission_PRNDM_Conf = 5;
  123.       }
  124.     }
  125.     if($sig_engine_PRNDM_Req == 0x2){ // If in neutral the gearbox is in neutral
  126.       $Transmission_Engine_1::sig_transmission_Gear1 = 0;
  127.       $Transmission_Engine_2::sig_transmission_Gear2 = 0;
  128.       $Transmission_Engine_2::sig_transmission_PRNDM_Conf = $sig_engine_PRNDM_Req;
  129.     }
  130.     if($sig_engine_PRNDM_Req == 0x3){ // If in drive the gearbox is in drive
  131.       $Transmission_Engine_2::sig_transmission_PRNDM_Conf = $sig_engine_PRNDM_Req;
  132.     }
  133.     if($sig_engine_PRNDM_Req == 0x4){ // If in manual shift the gearbox is in manual shift
  134.       $Transmission_Engine_2::sig_transmission_PRNDM_Conf = $sig_engine_PRNDM_Req;
  135.     }
  136.   }
  137. }
  138.  
  139.  
  140. void ManualShift()
  141. {
  142.   int condd[6], gear, acc;
  143.   $sig_engine_Man_Shift = 0x4;
  144.   if(state == 1){ // This is used to check if we are in park or neutral when switching to manual shifting
  145.     $Transmission_Engine_1::sig_transmission_Gear1 = 1; // Sets the vehicle in first gear
  146.     $Transmission_Engine_2::sig_transmission_Gear2 = 1;
  147.     state = 2; // Resets the state
  148.   }else{
  149.     // Check if there is an upshift request and that we can still upshift ( current gear is < 5)
  150.     if($sig_engine_Man_Shift == 0x1 && $Transmission_Engine_1::sig_transmission_Gear1 < 5){
  151.       gear = $Transmission_Engine_1::sig_transmission_Gear1 + 1; // Do the upshift
  152.       acc = ($sig_transmission_RPM * gearr[gear]) / 6; // Calculate the accelerator position for the upshift
  153.       if(acc > 50){ // Checks if the upshift is possible else we stick to the same gear.
  154.         @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[gear]) / 6; // Set the RPM for upshift
  155.         $sig_engine_Man_Shift == 0x4; // Reset the upshift flag to idle
  156.         $Transmission_Engine_1::sig_transmission_Gear1 = gear; // Confirm the upshift
  157.         $Transmission_Engine_2::sig_transmission_Gear2 = gear;
  158.         $sig_transmission_Conf_Up_Down = 1;
  159.       }else { // Else we stick to the same gear and reset the upshift request flag
  160.         gear = $Transmission_Engine_1::sig_transmission_Gear1 - 1;
  161.         $sig_engine_Man_Shift == 0x4;
  162.         $sig_transmission_Conf_Up_Down = 0;
  163.       }
  164.     }else if($sig_engine_Man_Shift == 0x0 && $Transmission_Engine_1::sig_transmission_Gear1 > 1) {
  165.       gear = $Transmission_Engine_1::sig_transmission_Gear1 - 1; // Do the downshift
  166.       acc = ($sig_transmission_RPM * gearr[gear]) / 6; // Calculate the accelerator position for the downshift
  167.       if(acc < 1000){ // Checks if the downshift is possible else we stick to the same gear.
  168.         @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[gear]) / 6; // Set the RPM for downshift
  169.         $sig_engine_Man_Shift == 0x4; // Reset the downshift flag to idle
  170.         $Transmission_Engine_1::sig_transmission_Gear1 = gear; // Confirm the downshift
  171.         $Transmission_Engine_2::sig_transmission_Gear2 = gear;
  172.         $sig_transmission_Conf_Up_Down = 1;
  173.       }else { // Else we stick to the same gear and reset the downshift request flag
  174.         gear = $Transmission_Engine_1::sig_transmission_Gear1 + 1;
  175.         $sig_engine_Man_Shift == 0x4;
  176.         $sig_transmission_Conf_Up_Down = 0;
  177.       }
  178.     }else{ // Calculate the output rpm based on the current engaged gear
  179.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[gear];
  180.     }
  181.   }
  182. }
  183.  
  184. void AutoShift()
  185. {
  186.   int cond[6], condd[6];
  187.   $sig_transmission_Conf_Up_Down = 1;
  188.  
  189.   //1st Gear
  190.   if($sig_transmission_RPM <= 490){
  191.     if(condd[1] == 1){
  192.       @sysvar::sysNS::sys_engine_Acceleratie = (483 * gearr[1]) / 6; // Set the RPM accordingly for downshifts, where 483 is the last possible rpm for 1st gear
  193.       condd[1] = 0; // Reset the downshift condition
  194.     }else{
  195.       $Transmission_Engine_1::sig_transmission_Gear1 = 1; // Engage 1st gear
  196.       $Transmission_Engine_2::sig_transmission_Gear2 = 1;
  197.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[1]; // calculate the rpm for 1st gear based on throttle input
  198.     }
  199.     if($sig_transmission_RPM >=480){
  200.       cond[2] = 1; // Set the upshift condition
  201.     }
  202.   }
  203.  
  204.   //2nd Gear
  205.   if($sig_transmission_RPM > 490 && $sig_transmission_RPM <= 970){
  206.     if(condd[2] == 1){
  207.       @sysvar::sysNS::sys_engine_Acceleratie = (951 * gearr[2]) / 6; // Set the RPM accordingly for downshift
  208.       condd[2] = 0; // Reset the downshift condition
  209.     }else if(cond[2] == 1){
  210.       @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[2]) / 6; // Set the RPM accordingly for upshift
  211.       cond[2] = 0; // Reset the upshift condition
  212.     }else{
  213.       $Transmission_Engine_1::sig_transmission_Gear1 = 2; // Engage 2nd gear
  214.       $Transmission_Engine_2::sig_transmission_Gear2 = 2;
  215.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[2]; // calculate the rpm for 2nd gear based on throttle input
  216.     }
  217.     if($sig_transmission_RPM >=950){
  218.       cond[3] = 1; // Set the upshift condition
  219.     }
  220.     if($sig_transmission_RPM <= 500){
  221.       condd[1] = 1; // Set the downshift condition
  222.     }
  223.   }
  224.  
  225. //3rd Gear
  226.   if($sig_transmission_RPM > 970 && $sig_transmission_RPM <= 1500){
  227.     if(condd[3] == 1){
  228.       @sysvar::sysNS::sys_engine_Acceleratie = (1485 * gearr[3]) / 6; // Set the RPM accordingly for downshift
  229.       condd[3] = 0; // Reset the downshift condition
  230.     }else if(cond[3] == 1){
  231.       @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[3]) / 6; // Set the RPM accordingly for upshift
  232.       cond[3] = 0; // Reset the upshift condition
  233.     }else{
  234.       $Transmission_Engine_1::sig_transmission_Gear1 = 3; // Engage 3rd gear
  235.       $Transmission_Engine_2::sig_transmission_Gear2 = 3;
  236.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[3]; // calculate the rpm for 3rd gear based on throttle input
  237.     }
  238.     if($sig_transmission_RPM >= 1480){
  239.       cond[4] = 1; // Set the upshift condition
  240.     }
  241.     if($sig_transmission_RPM <= 1000){
  242.       condd[2] = 1; // Set the downshift condition
  243.     }
  244.   }
  245.  
  246. //4th Gear
  247.   if($sig_transmission_RPM > 1500 && $sig_transmission_RPM <= 2000){
  248.     if(condd[4] == 1){
  249.       @sysvar::sysNS::sys_engine_Acceleratie = (1940 * gearr[4]) / 6; // Set the RPM accordingly for downshift
  250.       condd[4] = 0; // Reset the downshift condition
  251.     }else if(cond[4] == 1){
  252.       @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[4]) / 6; // Set the RPM accordingly for upshift
  253.       cond[4] = 0; // Reset the upshift condition
  254.     }else{
  255.       $Transmission_Engine_1::sig_transmission_Gear1 = 4; // Engage 4th gear
  256.       $Transmission_Engine_2::sig_transmission_Gear2 = 4;
  257.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[4]; // calculate the rpm for 4th gear based on throttle input
  258.     }
  259.     if($sig_transmission_RPM >= 1970){
  260.       cond[5] = 1; // Set the upshift condition
  261.     }
  262.     if($sig_transmission_RPM <= 1600){
  263.       condd[3] = 1; // Set the downshift condition
  264.     }
  265.   }
  266.  
  267. //5th Gear
  268.   if($sig_transmission_RPM > 2000){
  269.     if(cond[5] == 1){
  270.       @sysvar::sysNS::sys_engine_Acceleratie = ($sig_transmission_RPM * gearr[5]) / 6; // Set the RPM accordingly for upshift
  271.       cond[5] = 0; // Reset the upshift condition
  272.     }else{
  273.       $Transmission_Engine_1::sig_transmission_Gear1 = 5; // Engage 5th gear
  274.       $Transmission_Engine_2::sig_transmission_Gear2 = 5;
  275.       $Transmission_Engine_1::sig_transmission_RPM = $sig_engine_RPM / gearr[5]; // calculate the rpm for 5th gear based on throttle input
  276.     }
  277.     if($sig_transmission_RPM <= 2100){ // Downshift into 4th gear
  278.       condd[4] = 1;
  279.     }
  280.   }
  281. }
  282.  
  283.  
  284.  
  285. dword applILTxPending (long aId, dword aDlc, byte data[])
  286. {
  287.   dword i;
  288.   byte xor, crc;
  289.   int e2e_sqcvalue1, e2e_sqcvalue2;
  290.   //The following code increments the SQC value and then calculates the CRC of the 0x321 message.
  291.   if(aId == 0x321)
  292.   {
  293.     if(@sysvar::sysNS::E2E_TE1_MSG_Error == 0){
  294.       e2e_sqcvalue1 = data[1] & 0x0F;
  295.       //This code takes the value of the current sqc
  296.       if(@sysvar::sysNS::E2E_TE1_SQC_Error != 1){
  297.         e2e_sqcvalue1++;
  298.       }else{
  299.         e2e_sqcvalue1 = 0;
  300.       }
  301.       e2e_sqcvalue1 = e2e_sqcvalue1 & 0xF;
  302.       //This code increments the value of the sqc counter
  303.       data[1] = (data[1] & 0xF0) + e2e_sqcvalue1; //Append the new sqc value in the data of the message
  304.       xor = 0xFF;
  305.       for(i = 1; i < aDlc; ++i) {
  306.         xor = xor ^ data[i];
  307.         xor = crctable[xor];
  308.       }
  309.       xor = xor ^ dataid[e2e_sqcvalue1];
  310.       crc = crctable[xor];
  311.       //Calculate the CRC of the message without the final 0xFF xor.
  312.       data[0] = crc ^ 0xFF;
  313.       if(@sysvar::sysNS::E2E_TE1_CRC_Error == 1){
  314.         data[0] = data[0] ^ 0xFF;
  315.       }
  316.     }else{
  317.       return 0;
  318.     }
  319.   }
  320.   //The following code does the same for the 0x322 message, incrementing the sqc value and then calculating the CRC
  321.   if(aId == 0x322)
  322.   {
  323.     if(@sysNS::E2E_TE2_MSG_Error == 0){
  324.       e2e_sqcvalue2 = data[1] & 0x0F;
  325.       if(@sysvar::sysNS::E2E_TE2_SQC_Error != 1){
  326.         e2e_sqcvalue2++;
  327.       }else{
  328.         e2e_sqcvalue1 = 0;
  329.       }
  330.       e2e_sqcvalue2 = e2e_sqcvalue2 & 0xF;
  331.       data[1] = (data[1] & 0xF0) + e2e_sqcvalue2;
  332.       xor = 0xFF;
  333.       for(i = 1; i < aDlc; ++i) {
  334.         xor = xor ^ data[i];
  335.         crc = crctable[xor];
  336.       }
  337.       xor = xor ^ dataid[e2e_sqcvalue2];
  338.       crc = crctable[xor];
  339.       data[0] = crc ^ 0xFF;
  340.       if(@sysvar::sysNS::E2E_TE2_CRC_Error == 1){        
  341.         data[0] = data[0] ^ 0xFF;
  342.       }
  343.     }else{
  344.       return 0;
  345.     }
  346.   }
  347.   return 1; //Returning 1 means allowing
  348. }
  349.  
  350. int E2ECheck(dword aId, byte aDlc, byte data[])
  351. {
  352.   dword i;
  353.   byte xor, crc, crc_123, crc_124;
  354.   int e2e_c_sqcold1, e2e_c_sqcold2;
  355.   int e2e_c_sqcvalue1, e2e_c_sqcvalue2, ver;
  356.   ver = 0;
  357.   if(aId == 0x123)
  358.   {
  359.     e2e_c_sqcvalue1 = data[1] & 0x0F;
  360.     if(((e2e_c_sqcold1 + 1) & 0xF) == e2e_c_sqcvalue1){
  361.       xor = 0xFF;
  362.       for(i = 1; i < aDlc; ++i) {
  363.         xor = xor ^ data[i];
  364.         xor = crctable[xor];
  365.       }
  366.       xor = xor ^ dataid[e2e_c_sqcvalue1];
  367.       crc = crctable[xor];
  368.       crc_123 = crc ^ 0xFF;
  369.       if(data[0] == crc_123){
  370.         ver = 1;
  371.         e2e_c_sqcold1 = e2e_c_sqcvalue1;
  372.         sysSetVariableString(sysvar::sysNS::E2E_ET1_Status, "OK");
  373.       }else {
  374.         ver = 0;
  375.         e2e_c_sqcold1 = e2e_c_sqcvalue1;
  376.         sysSetVariableString(sysvar::sysNS::E2E_ET1_Status, "CRC Error");
  377.       }
  378.     }else {
  379.       ver = 0;
  380.       sysSetVariableString(sysvar::sysNS::E2E_ET1_Status, "SQC Error");
  381.     }
  382.   }
  383.   if(aId == 0x124)
  384.   {
  385.     e2e_c_sqcvalue2 = data[1] & 0x0F;
  386.     if(((e2e_c_sqcold2 + 1) & 0xF) == e2e_c_sqcvalue2){
  387.       xor = 0xFF;
  388.       for(i = 1; i < aDlc; ++i) {
  389.         xor = xor ^ data[i];
  390.         crc = crctable[xor];
  391.       }
  392.       xor = xor ^ dataid[e2e_c_sqcvalue2];
  393.       crc = crctable[xor];
  394.       crc_124 = crc ^ 0xFF;
  395.       if(data[0] == crc_124){
  396.         ver = 1;
  397.         e2e_c_sqcold2 = e2e_c_sqcvalue2;
  398.         sysSetVariableString(sysvar::sysNS::E2E_ET2_Status, "OK");
  399.       }else {
  400.         ver = 0;
  401.         e2e_c_sqcold2 = e2e_c_sqcvalue2;
  402.         sysSetVariableString(sysvar::sysNS::E2E_ET2_Status, "CRC Error");
  403.       }
  404.     }else {
  405.       ver = 0;
  406.       sysSetVariableString(sysvar::sysNS::E2E_ET2_Status, "SQC Error");
  407.     }
  408.   }
  409.   return ver;
  410. }
  411.  
  412.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement