极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 8144|回复: 7

gencode——一个根据XML协议描述自动生成代码的小工具

[复制链接]
发表于 2012-11-30 23:23:57 | 显示全部楼层 |阅读模式
先看下笔者的XML协议描述:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <metalib>
  3.     <macro name="HEAD_SIZE" value="3" />
  4.     <macro name="MAX_BUFFER_SIZE" value="60" />
  5.    
  6.     <macro name="FORWARD_DIR" value="0" />
  7.     <macro name="BACKWARD_DIR" value="1" />
  8.     <macro name="STOP_DIR" value="2" />
  9.    
  10.     <macro name="PWM_ENA_PIN" value="3" />
  11.     <macro name="PWM_ENB_PIN" value="5" />
  12.     <macro name="PWM_IN1_PIN" value="8" />
  13.     <macro name="PWM_IN2_PIN" value="7" />
  14.     <macro name="PWM_IN3_PIN" value="12" />
  15.     <macro name="PWM_IN4_PIN" value="11" />
  16.    
  17.     <macro name="LINE_MOVE_CMD" value="100" />
  18.     <macro name="MOVE_CMD" value="101" />
  19.    
  20.     <struct name="LineMove">
  21.         <field name="dir" type="uchar" />
  22.         <field name="speed" type="uchar" />
  23.     </struct>
  24.     <struct name="Move">
  25.         <field name="left_dir" type="uchar" />
  26.         <field name="left_speed" type="uchar" />
  27.         <field name="right_dir" type="uchar" />
  28.         <field name="right_speed" type="uchar" />
  29.     </struct>
  30.     <struct name="Head">
  31.         <field name="cmd" type="uchar" />
  32.         <field name="body_len" type="ushort" />
  33.     </struct>
  34.    
  35. </metalib>
复制代码
通过gencode.exe程序生成的代码:
proto.h
  1. /*This head file is generated by code. Please do not edit!*/
  2. #ifndef proto_H
  3. #define proto_H
  4. #ifdef __cplusplus
  5. extern "C" {
  6. #endif

  7. #define HEAD_SIZE 3
  8. #define MAX_BUFFER_SIZE 60
  9. #define FORWARD_DIR 0
  10. #define BACKWARD_DIR 1
  11. #define STOP_DIR 2
  12. #define PWM_ENA_PIN 3
  13. #define PWM_ENB_PIN 5
  14. #define PWM_IN1_PIN 8
  15. #define PWM_IN2_PIN 7
  16. #define PWM_IN3_PIN 12
  17. #define PWM_IN4_PIN 11
  18. #define LINE_MOVE_CMD 100
  19. #define MOVE_CMD 101

  20. #pragma pack(1)
  21. typedef struct {
  22.     unsigned char dir;
  23.     unsigned char speed;
  24. }LineMove;
  25. extern inline int serialize_LineMove(LineMove *linemove, void *buffer, const int offset);
  26. extern inline int deserialize_LineMove(void *buffer, LineMove *linemove, const int offset);

  27. typedef struct {
  28.     unsigned char left_dir;
  29.     unsigned char left_speed;
  30.     unsigned char right_dir;
  31.     unsigned char right_speed;
  32. }Move;
  33. extern inline int serialize_Move(Move *move, void *buffer, const int offset);
  34. extern inline int deserialize_Move(void *buffer, Move *move, const int offset);

  35. typedef struct {
  36.     unsigned char cmd;
  37.     unsigned short body_len;
  38. }Head;
  39. extern inline int serialize_Head(Head *head, void *buffer, const int offset);
  40. extern inline int deserialize_Head(void *buffer, Head *head, const int offset);

  41. static int serialize(void *stru, void *buffer, int size, const int offset);
  42. static int deserialize(void *buffer, void *stru, int size, const int offset);

  43. #pragma pack()
  44. #ifdef __cplusplus
  45. }
  46. #endif
  47. #endif
复制代码
proto.c
  1. /*This source file is generated by code. Please do not edit!*/
  2. #include "proto.h"

  3. static int serialize(void *stru, void *buffer, int size, const int offset)
  4. {
  5.     char *p_stru = (char *)stru;
  6.     char *p_buffer = (char *)(buffer + offset);
  7.    
  8.     while(*p_buffer++ = *p_stru++, size--);
  9.    
  10.     return size + offset;
  11. }
  12. static int deserialize(void *buffer, void *stru, int size, const int offset)
  13. {
  14.     char *p_stru = (char *)stru;
  15.     char *p_buffer = (char *)(buffer + offset);

  16.     while(*p_stru++ = *p_buffer++, size--);
  17.    
  18.     return size + offset;
  19. }

  20. inline int serialize_LineMove(LineMove *linemove, void *buffer, const int offset)
  21. {
  22.     return serialize(linemove, buffer, sizeof(*linemove), offset);
  23. }

  24. inline int deserialize_LineMove(void *buffer, LineMove *linemove, const int offset)
  25. {
  26.     return deserialize(buffer, linemove, sizeof(*linemove), offset);
  27. }

  28. inline int serialize_Move(Move *move, void *buffer, const int offset)
  29. {
  30.     return serialize(move, buffer, sizeof(*move), offset);
  31. }

  32. inline int deserialize_Move(void *buffer, Move *move, const int offset)
  33. {
  34.     return deserialize(buffer, move, sizeof(*move), offset);
  35. }

  36. inline int serialize_Head(Head *head, void *buffer, const int offset)
  37. {
  38.     return serialize(head, buffer, sizeof(*head), offset);
  39. }

  40. inline int deserialize_Head(void *buffer, Head *head, const int offset)
  41. {
  42.     return deserialize(buffer, head, sizeof(*head), offset);
  43. }
复制代码
下面是使用以上生成代码的Arduino主文件sample.ino
  1. /** 示例
  2.     By Apache([email protected]) 2012/11/30
  3. */
  4. #include "proto.h"

  5. #define SET_LEFT_PWM(SPEED) analogWrite(PWM_ENB_PIN, (SPEED))
  6. #define SET_RIGHT_PWM(SPEED) analogWrite(PWM_ENA_PIN, (SPEED))
  7. #define SET_DIR(DIR, XPIN, YPIN) \
  8.     do { \
  9.         if(DIR != STOP_DIR) {\
  10.             digitalWrite(XPIN, (DIR)); \
  11.             digitalWrite(YPIN, !(DIR)); \
  12.         }else {\
  13.             digitalWrite(XPIN, 0); \
  14.             digitalWrite(YPIN, 0); \
  15.         } \
  16.     }while(0)
  17.    
  18. #define SET_LEFT_DIR(DIR) SET_DIR(DIR, PWM_IN3_PIN, PWM_IN4_PIN)
  19. #define SET_RIGHT_DIR(DIR) SET_DIR(DIR, PWM_IN1_PIN, PWM_IN2_PIN)

  20. #define SET_MOTOR(LSPD, LDIR, RSPD, RDIR) \
  21.     do { \
  22.         SET_LEFT_PWM((LSPD)); \
  23.         SET_RIGHT_PWM((RSPD)); \
  24.         SET_LEFT_DIR((LDIR)); \
  25.         SET_RIGHT_DIR((RDIR)); \
  26.     }while(0)
  27.    
  28. static uint8_t waiting_head = 1;
  29. static Head head;

  30. void init_pin()
  31. {
  32.     uint8_t outputs[] = {
  33.         PWM_ENA_PIN, PWM_IN1_PIN, PWM_IN2_PIN,
  34.         PWM_ENB_PIN, PWM_IN3_PIN, PWM_IN4_PIN };
  35.     uint8_t inputs[] = {};
  36.     uint8_t i, ocnt = sizeof(outputs), icnt = sizeof(inputs);
  37.    
  38.     for(i = 0; i < ocnt; i++) {
  39.         pinMode(outputs[i], OUTPUT);
  40.     }
  41.     for(i = 0; i < icnt; i++) {
  42.         pinMode(inputs[i], INPUT);
  43.     }
  44. }
  45. void setup()
  46. {
  47.     Serial.begin(9600);
  48.     init_pin();
  49. }

  50. void on_line_move(uint8_t *buffer)
  51. {
  52.     LineMove linemove;
  53.    
  54.     deserialize_LineMove(buffer, &linemove, 0);
  55.     SET_MOTOR(linemove.speed, linemove.dir, linemove.speed, linemove.dir);
  56. }

  57. void on_move(uint8_t *buffer)
  58. {
  59.     Move move;
  60.    
  61.     deserialize_Move(buffer, &move, 0);
  62.     SET_MOTOR(move.left_speed, move.left_dir, move.right_speed, move.right_dir);
  63. }

  64. void proto_process(uint32_t cmd, uint8_t *buffer)
  65. {
  66.     switch(cmd) {
  67.         case LINE_MOVE_CMD:
  68.             on_line_move(buffer);
  69.             break;
  70.         case MOVE_CMD:
  71.             on_move(buffer);
  72.             break;
  73.         default:
  74.             break;
  75.     }
  76. }
  77. void recv_package()
  78. {
  79.     int nbytes;
  80.     uint8_t buffer[MAX_BUFFER_SIZE] = "";
  81.    
  82.     nbytes = Serial.available();
  83.     if(waiting_head) {
  84.         if(nbytes >= HEAD_SIZE) {
  85.             Serial.readBytes((char *)buffer, HEAD_SIZE);
  86.             deserialize_Head(buffer, &head, 0);
  87.             waiting_head = 0;
  88.         }
  89.     }
  90.     else {
  91.         if(nbytes >= head.body_len) {
  92.             Serial.readBytes((char *)buffer, head.body_len);
  93.             proto_process(head.cmd, buffer);
  94.             waiting_head = 1;
  95.         }
  96.     }
  97. }
  98. void loop()
  99. {
  100.     recv_package();
  101. }
复制代码
下面是自动生成的python代码:
proto.py
  1. #-*- coding: utf-8 -*-
  2. # This script is generated by code. Please do not edit!

  3. from struct import pack, unpack

  4. PACK_TYPE_DICT = {
  5.     "char": ("b", 1),
  6.     "uchar": ("B", 1),
  7.     "short": ("h", 2),
  8.     "ushort": ("H", 2),
  9.     "int": ("i", 4),
  10.     "uint": ("I", 4),
  11.     "long": ("l", 4),
  12.     "ulong": ("L", 4),
  13.     "longlong": ("q", 8),
  14.     "ulonglong": ("Q", 8),
  15.     "float": ("f", 4),
  16.     "double": ("d", 8),
  17. }

  18. class Codec:
  19.     @classmethod
  20.     def serialize(self, obj):
  21.         __buffer = []
  22.         for field_pktype in obj._field_pktype:
  23.             field, pktype = field_pktype[0], PACK_TYPE_DICT[field_pktype[1]][0]
  24.             value = obj.__dict__.get(field)
  25.             if not value: value = 0
  26.             __buffer.append(pack(pktype, value))
  27.         
  28.         return ''.join(__buffer)

  29.     @classmethod
  30.     def deserialize(self, obj, buffer):
  31.         offset = 0
  32.         for field_pktype in obj._field_pktype:
  33.             field = field_pktype[0]
  34.             pktype, size = PACK_TYPE_DICT[field_pktype[1]]
  35.             obj.__dict__[field] = unpack(pktype, buffer[offset: offset + size])[0]
  36.             offset += size
  37.             
  38.         return offset


  39. class MetaClass(type):
  40.     def __init__(cls, name, bases, dict):
  41.         def _init(cls, **_dict):
  42.             for field, val in _dict.items():
  43.                 setattr(cls, field, val)
  44.         def _serialize(cls, **args):
  45.             return dict["_codec"].serialize(cls, **args)
  46.         def _deserialize(cls, buffer, **args):
  47.             return dict["_codec"].deserialize(cls, buffer, **args)
  48.         
  49.         for field, _ in dict["_fields"]:
  50.             setattr(cls, field, None)
  51.             
  52.         setattr(cls, "__init__", _init)
  53.         setattr(cls, "_field_pktype", dict["_fields"])
  54.         setattr(cls, "serialize", _serialize)
  55.         setattr(cls, "deserialize", _deserialize)
  56.         
  57.         delattr(cls, "_fields")
  58.         delattr(cls, "_codec")
  59.         
  60.         super(MetaClass, cls).__init__(name, bases, dict)

  61. HEAD_SIZE = 3
  62. MAX_BUFFER_SIZE = 60
  63. FORWARD_DIR = 0
  64. BACKWARD_DIR = 1
  65. STOP_DIR = 2
  66. PWM_ENA_PIN = 3
  67. PWM_ENB_PIN = 5
  68. PWM_IN1_PIN = 8
  69. PWM_IN2_PIN = 7
  70. PWM_IN3_PIN = 12
  71. PWM_IN4_PIN = 11
  72. LINE_MOVE_CMD = 100
  73. MOVE_CMD = 101

  74. LineMove = MetaClass(
  75. name="LineMove",
  76. bases=(),
  77. dict=dict(
  78.     _codec=Codec,
  79.     _fields=(
  80.             (u'dir', u'uchar'),
  81.             (u'speed', u'uchar')
  82.         )
  83. )
  84. )

  85. Move = MetaClass(
  86. name="Move",
  87. bases=(),
  88. dict=dict(
  89.     _codec=Codec,
  90.     _fields=(
  91.             (u'left_dir', u'uchar'),
  92.             (u'left_speed', u'uchar'),
  93.             (u'right_dir', u'uchar'),
  94.             (u'right_speed', u'uchar')
  95.         )
  96. )
  97. )

  98. Head = MetaClass(
  99. name="Head",
  100. bases=(),
  101. dict=dict(
  102.     _codec=Codec,
  103.     _fields=(
  104.             (u'cmd', u'uchar'),
  105.             (u'body_len', u'ushort')
  106.         )
  107. )
  108. )
复制代码
烧进去代码的后,我使用上面的py文件写了一个测试脚本:
test_proto.py
  1. #-*- coding: utf-8 -*-
  2. #示例, 测试LineMove协议
  3. #By Apache([email protected]) 2012/11/20

  4. import sys
  5. try:
  6.     import serial
  7. except ImportError:
  8.     print "Import serial error"
  9.     sys.exit(1)
  10. import proto
  11. from time import sleep

  12. COM = "COM3"
  13. BAUND=9600

  14. print u"正在打开串口..."
  15. try:
  16.     ser = serial.Serial(COM, BAUND)
  17.     sleep(2)
  18. except Exception, e:
  19.     print e
  20.     sys.exit(1)
  21. print u"已打开, 开始协议测试..."

  22. class TestLineMove:
  23.     def __init__(self):
  24.         self.head = proto.Head(cmd=proto.LINE_MOVE_CMD)
  25.         self.body = proto.LineMove()
  26.    
  27.     def __sendpkg(self):
  28.         body_buffer = self.body.serialize()
  29.         self.head.body_len = len(body_buffer)
  30.         head_buffer = self.head.serialize()
  31.         ser.write("%s%s" % (head_buffer, body_buffer))
  32.         
  33.     def __line_move(self, msg, dir=0, speed=100, t=2):
  34.         self.body.dir = dir
  35.         self.body.speed = speed
  36.         print msg
  37.         self.__sendpkg()
  38.         sleep(t)
  39.         
  40.     def test(self):
  41.         print u"LineMove协议测试"
  42.         self.__line_move(u"低速前进两秒", dir=0, speed=100, t=2)
  43.         self.__line_move(u"高速前进两秒", dir=0, speed=200, t=2)
  44.         self.__line_move(u"低速后退两秒", dir=1, speed=100, t=2)
  45.         self.__line_move(u"高速后退两秒", dir=1, speed=200, t=2)
  46.         self.__line_move(u"停止", dir=2, speed=0, t=0)

  47. if __name__ == "__main__":
  48.     TestLineMove().test()
  49.    
  50. ser.close()
复制代码
运行该脚本,测试效果良好,以后可以不用总是发ASCII码了……

下面是gencode的使用帮助:

更多的详细说明和示例代码见附件:

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2012-12-1 00:03:41 | 显示全部楼层
高{:soso_e142:}
回复 支持 反对

使用道具 举报

发表于 2012-12-1 08:38:29 | 显示全部楼层
哈哈哈这个强
回复 支持 反对

使用道具 举报

发表于 2012-12-1 12:05:08 | 显示全部楼层
有点一次描述,到处编译的味道,呵呵,XML的文档写写啊
回复 支持 反对

使用道具 举报

发表于 2012-12-2 18:22:05 | 显示全部楼层
没看懂  能解释一下否?
回复 支持 反对

使用道具 举报

发表于 2012-12-2 22:22:41 | 显示全部楼层
很牛逼的工具,可以自己修改下,生成其他的代码吗?
楼主能不能整个详细的教程?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-12-3 10:23:09 | 显示全部楼层
acbear 发表于 2012-12-2 22:22
很牛逼的工具,可以自己修改下,生成其他的代码吗?
楼主能不能整个详细的教程?

完全可以,只要按照示例,自己可以随意修改XML协议,然后通过gencode生成代码
这个工具的目的就是将程序的数据层和逻辑层分离
方便我们更快的开发上位机与Arduino的通信程序~

详细的教程的话,我暂时没有时间写,并且个人觉得示例已经写的很清楚了
如果大家有些Protocol buffers的经验会更容易理解

其它不理解的地方可以一起回帖交流

这版本还比较初级,如果需要源码可以email我索取
回复 支持 反对

使用道具 举报

发表于 2012-12-3 12:55:34 | 显示全部楼层
apache 发表于 2012-12-3 10:23
完全可以,只要按照示例,自己可以随意修改XML协议,然后通过gencode生成代码
这个工具的目的就是将程序 ...

请发我一份,多谢!
[email protected]
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-4 17:54 , Processed in 0.042211 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表