“頂嵌杯”決賽第2題公佈

題目名稱:模擬Modbus協議

Description

在嵌入式系統開發中,Modbus協議是工業控制系統中廣泛應用的一種協議。本題用來簡單模擬Modbus協議,只需根據條件生成符合該協議的數據幀,並解析所獲取的數據。
假設設備使用的協議發送數據格式如下:

<SlaveAddress, 1 Byte> <Function, 1 Byte> <Start Address, 2 Bytes> <NumberofBytes, 2 Bytes> <Checksum, 2 Bytes>
其中前四項將在輸入條件中給出,最後一項爲CRC校驗和,需根據前四項的數據,按照CRC算法進行計算。注意數據的長度,多於1byte的高位在前,低位在後。該CRC校驗算法的描述如下:

1)
CRC賦值0xFFFF

2)
取初始信息的第一個字節(8位)與CRC進行異或運算,將結果賦給CRC

3)
CRC數據右移一位,最前位(左邊)補0

4)
如果右移前,CRC最低位(最右端)爲1,則將右移後的CRC0xA001進行異或運算,且將結果賦給CRC。否則,跳過此步。

5)
重複348次(即右邊8位)。

6)
對初始信息的下一個字節,同樣執行2345步,直到信息中所有字節都執行了同樣的步驟。

7)
將此時得到的CRC值的高8位和低8位交換,即得到CRC校驗和。


對應的接收格式如下:

<SlaveAddress,1Byte> <Function,1Byte> <NumberofBytes,1Byte> <DataIEEE32,xByte> <Checksum,2Bytes>
其中DataIEEE32爲一個或多個按IEEE754標準定義的32位浮點數,具體的數據長度由NumberofBytes項來決定(比如NumberofBytes4,則DataIEEE32項爲4 bytes,正好表示一個浮點數;如爲8,則DataIEEE32項爲8 bytes,可表示兩個浮點數)。本題要求編程實現從IEEE32數據(如“420B999A”)到浮點數(如34.9)的轉換,從而解析出浮點數值。


提示:你可以根據IEEE754標準自行設計轉換算法;或者直接利用C語言float類型的實現特性:x86 linux下,gcc編譯器將C語言代碼“float f = 34.9;”編譯成彙編代碼“movl $0x420b999a, -4(%ebp)” AT&T x86彙編格式),也就是說,單精度浮點數34.9在內存中就是由整數0x420b999a來表示的,你可以利用這一特性來完成轉換。

Input

輸入包含多組數據,以EOF結束
每組數據共兩行。

第一行共四個十進制整數,分別爲協議格式要求的:<SlaveAddress, 1 Byte>,<Function, 1 Byte>,<Start Address, 2 Bytes>,<NumberofBytes, 2 Bytes>,以逗號“,”分開。

如:
1,4,40,2
其中:1SlaveAddress4Function40Start Address2NumberofBytes

第二行爲符合接收格式的數據幀(16進製表示),需從其中解析所接收的數據,其長度小於64個字符,浮點數數據最多爲4個(即DataIEEE32數據項最多爲32bytes)。

: 010404420B999A7405
其中:01SlaveAddress04Function04NumberofBytes 420B999ADataIEEE327405Checksum

Output

每組數據輸出共兩行。
第一行:根據輸入結果的第一行,輸出完整的符合該協議發送格式的數據幀,數據用16進制大寫表示,每部分的長度都要求符合協議格式,比如Start Address項如果不到2 bytes,則需要在左邊補零。

如:010400280002F1C
3
其中:01SlaveAddress04 Function0028Start Address0002NumberofBytesF1C3Checksum


第二行:根據輸入結果的第二行,依次解析IEEE32數據,將其轉換成浮點數並打印結果(小數點後保留一位)。解析之前需檢查CRC校驗和,如校驗失敗則直接打印CRC_ERROR。如有多個數據,用逗號分隔。

如:
34.9
該浮點值爲420B999A所對應的值。

Sample Input

1,4,40,2 

010404420B999A7405 

1,4,40,2

010404420B999A7404

2,4,383,4

02040841CC0000477F2100DF85

 

 

Sample Output

010400280002F1C3

34.9

010400280002F1C3

CRC_ERROR

0204017F0004C1DE

25.5,65313.0

Source  (參考代碼)

#include <stdio.h>

#include <string.h>

 

#define MAX_RECEIVE_LEN 65

 

int char2int(char c)

{

       if(c >= '0' && c <= '9') return c - '0';

       return c - 'A' + 10;

}

 

unsigned short cal_crc(unsigned char* p,int len)

{

       unsigned short ret = 0xFFFF;

       int i = 0,k = 0;

       for(;i < len;++i)

       {

              ret ^= p[i];

              for(k = 0;k < 8;++k)

              {

                     ret = (ret&0x01)?((ret>>1)^0xA001):(ret>>1);

              }

       }

       ret = ((ret&0x00FF)<<8)|((ret&0xFF00)>>8);

       return ret;

}

 

int main()

{

       int saveadd = 0,func = 0,startadd = 0,num = 0,i = 0,len = 0,k = 0;

       unsigned short val = 0;

       unsigned char receive[MAX_RECEIVE_LEN];

       unsigned char receivetmp[MAX_RECEIVE_LEN];

       unsigned char str[MAX_RECEIVE_LEN];

       unsigned char fstr[4];

       float * pf = NULL;

       while(EOF != scanf("%d,%d,%d,%d",&saveadd,&func,&startadd,&num))

       {

              str[0] = saveadd;str[1] = func;

              str[3] = startadd&0xFF;startadd >>= 8;str[2] = startadd&0xFF;

              str[5] = num&0xFF;num >>= 8;str[4] = num&0xFF;

              val = cal_crc(str,6);

              str[7] = val&0xFF;val >>= 8;str[6] = val&0xFF;

              for(i = 0;i < 8;++i) printf("%02X",str[i]);

              printf("/n");

              scanf("%s",receivetmp);

              len = strlen((char*)receivetmp)/2;

              for(i = 0;i < len;++i)

              {

                     receive[i] = (unsigned char)(char2int(receivetmp[i*2])*16+char2int(receivetmp[i*2+1]));

              }

              val = cal_crc(receive,len-2);

              if((val&0xFF) != receive[len-1]||((val>>8)&0xFF) != receive[len-2]) { printf("CRC_ERROR/n");continue; }

              k = 2;num = receive[k]/4;++k;

              for(i = 0;i < num;++i)

              {

                     fstr[0] = receive[k+3];fstr[1] = receive[k+2];

                     fstr[2] = receive[k+1];fstr[3] = receive[k];

                     pf = (float*)(fstr);

                     printf("%.1f",*pf);

                     if(i != num - 1) printf(",");

                     k += 4;

              }

              if(0 != num) printf("/n");

       }

       return 0;

}

 

注:該代碼來自參賽選手北京瑞星研發工程師曾劍傑,被“頂嵌杯”專家評審組公認爲符合工程化的高質量代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章