const protobuf = require('protobufjs'); const parseArgs = require('minimist'); const fs = require('fs'); const assert = require('assert'); class PBTools { constructor() { this.csMsgIdPb = null; this.csProtoPb = null; this.ssMsgIdPb = null; this.ssProtoPb = null; this.mtPb = null; this.protoDir = './proto/'; } async loadCsPb() { if (!fs.existsSync(this.protoDir + 'cs_proto.proto')) { console.error('cs_proto.proto file not exists'); return; } { this.csProtoPb = await (new protobuf.Root()).load( this.protoDir + 'cs_proto.proto', { 'keepCase': true } ); } { this.csMsgIdPb = await (new protobuf.Root()).load( this.protoDir + 'cs_msgid.proto', { 'keepCase': true } ); this.csCmMsgId = this.csMsgIdPb.lookup('CMMessageId_e'); this.csSmMsgId = this.csMsgIdPb.lookup('SMMessageId_e'); } } async loadSsPb() { if (!fs.existsSync(this.protoDir + 'ss_proto.proto')) { console.error('ss_proto.proto file not exists'); return; } { this.ssProtoPb = await (new protobuf.Root()).load( this.protoDir + 'ss_proto.proto', { 'keepCase': true } ); } { this.ssMsgIdPb = await (new protobuf.Root()).load( this.protoDir + 'ss_msgid.proto', { 'keepCase': true } ); this.ssSSmMsgId = this.ssMsgIdPb.lookup('SSMMessageId_e'); } } async loadMtPb() { if (!fs.existsSync(this.protoDir + 'mt.proto')) { console.error('mt.proto file not exists'); return; } { this.mtPb = await (new protobuf.Root()).load( this.protoDir + 'mt.proto', { 'keepCase': true } ); } } async init() { await this.loadCsPb(); await this.loadSsPb(); await this.loadMtPb(); if (this.csProtoPb) { await this.genCsAutoGen(); } if (this.mtPb) { await this.genMtbAutoGen(); } if (this.ssProtoPb) { await this.genSsAutoGen(); } } async genCsAutoGen() { let data = `package cs import ( "f5" proto "github.com/golang/protobuf/proto" ) type CsNetMsgHandler f5.NetMsgHandler[MsgHandler]; type MsgHandlerImpl struct { } var handlers [2000]*CsNetMsgHandler func GetNetMsgHandler(msgId uint16) *CsNetMsgHandler { handler := handlers[msgId] return handler } func DispatchMsg(handler *CsNetMsgHandler, hdr *f5.MsgHdr, msgHandler MsgHandler) { handler.Cb(hdr, msgHandler) } func RegHandlerId(msgId int, handlerId int) { handler := handlers[msgId] handler.HandlerId = handlerId } func ParsePb(msgId uint16, data []byte) interface{} { handler := handlers[msgId] if handler == nil { return nil } return handler.ParseCb(data) } `; data += ` type MsgHandler interface {`; this.csProtoPb.nested.cs.nestedArray.forEach( (item) => { if (item.name[0] == 'C' && item.name[1] == 'M') { data += ` ${item.name}(*f5.MsgHdr, *${item.name})`; } }); data += ` } `; this.csProtoPb.nested.cs.nestedArray.forEach( (item) => { if (item.name[0] == 'C' && item.name[1] == 'M') { data += ` func (this *MsgHandlerImpl) ${item.name}(hdr *f5.MsgHdr, msg *${item.name}) { } `; } }); this.csProtoPb.nested.cs.nestedArray.forEach( (item) => { if (item.name[0] == 'C' && item.name[1] == 'M') { data += ` func (this *${item.name}) GetNetMsgId() uint16 { return uint16(CMMessageIdE__${item.name}) } `; } else if (item.name[0] == 'S' && item.name[1] == 'M') { data += ` func (this *${item.name}) GetNetMsgId() uint16 { return uint16(SMMessageIdE__${item.name}) } `; } }); this.csProtoPb.nested.cs.nestedArray.forEach( (item) => { if (item.name[0] == 'S' && item.name[1] == 'M') { let hasErrCodeField = false; let hasErrMsgField = false; item.fieldsArray.forEach( (item2) => { if (item2.name == 'errcode') { hasErrCodeField = true; } else if (item2.name == 'errmsg') { hasErrMsgField = true; } }); if (hasErrCodeField && hasErrMsgField) { data += ` func (this *${item.name}) Err(errCode int32, errMsg string) *${item.name} { this.Errcode = proto.Int32(errCode) this.Errmsg = proto.String(errMsg) return this } `; } } }); data += ` func init() { `; this.csProtoPb.nested.cs.nestedArray.forEach( (item) => { if (item.name[0] == 'C' && item.name[1] == 'M') { data += ` handlers[int(CMMessageIdE__${item.name})] = &CsNetMsgHandler{ MsgId: int(CMMessageIdE__${item.name}), ParseCb: func (data []byte) interface{} { msg := &${item.name}{} proto.Unmarshal(data, msg) return msg }, Cb: func (hdr *f5.MsgHdr, handler MsgHandler) { handler.${item.name}(hdr, hdr.Msg.(*${item.name})) }, } `; } }); data += ` }`; fs.writeFileSync('./cs/cs.auto_gen.go', data); } async genSsAutoGen() { let data = `package ss import ( "f5" proto "github.com/golang/protobuf/proto" ) type SsNetMsgHandler f5.NetMsgHandler[MsgHandler]; type MsgHandlerImpl struct { } var handlers [2000]*SsNetMsgHandler func GetNetMsgHandler(msgId uint16) *SsNetMsgHandler { handler := handlers[msgId] return handler } func DispatchMsg(handler *SsNetMsgHandler, hdr *f5.MsgHdr, msgHandler MsgHandler) { handler.Cb(hdr, msgHandler) } func RegHandlerId(msgId int, handlerId int) { handler := handlers[msgId] handler.HandlerId = handlerId } func ParsePb(msgId uint16, data []byte) interface{} { handler := handlers[msgId] if handler == nil { return nil } return handler.ParseCb(data) } `; data += ` type MsgHandler interface {`; this.ssProtoPb.nested.ss.nestedArray.forEach( (item) => { if (item.name[0] == 'S' && item.name[1] == 'S') { data += ` ${item.name}(*f5.MsgHdr, *${item.name})`; } }); data += ` } `; this.ssProtoPb.nested.ss.nestedArray.forEach( (item) => { if (item.name[0] == 'S' && item.name[1] == 'S') { data += ` func (this *MsgHandlerImpl) ${item.name}(hdr *f5.MsgHdr, msg *${item.name}) { } `; } }); this.ssProtoPb.nested.ss.nestedArray.forEach( (item) => { if (item.name[0] == 'S' && item.name[1] == 'S') { data += ` func (this *${item.name}) GetNetMsgId() uint16 { return uint16(SSMessageIdE__${item.name}) } `; } else if (item.name[0] == 'S' && item.name[1] == 'M') { data += ` func (this *${item.name}) GetNetMsgId() uint16 { return uint16(SMMessageIdE__${item.name}) } `; } }); data += ` func init() { `; this.ssProtoPb.nested.ss.nestedArray.forEach( (item) => { if (item.name[0] == 'S' && item.name[1] == 'S') { data += ` handlers[int(SSMessageIdE__${item.name})] = &SsNetMsgHandler{ MsgId: int(SSMessageIdE__${item.name}), ParseCb: func (data []byte) interface{} { msg := &${item.name}{} proto.Unmarshal(data, msg) return msg }, Cb: func (hdr *f5.MsgHdr, handler MsgHandler) { handler.${item.name}(hdr, hdr.Msg.(*${item.name})) }, } `; } }); data += ` }`; fs.writeFileSync('./ss/ss.auto_gen.go', data); } async genMtbAutoGen() { let data = `package mtb import ( "f5" ) `; this.mtPb.nested.mt.nestedArray.forEach( (item) => { data += `type ${item.name} struct {\n`; item.fieldsArray.forEach ( (item2, index) => { assert(item2.id <= 127); const newFieldName = this.keyWordReplace(item2.name); data += ` ${newFieldName} ` + this.dumpClassField(item, item2, index) + `\n`; } ); data += ` _flags1_ uint64 _flags2_ uint64 } `; } ); data += ''; this.mtPb.nested.mt.nestedArray.forEach( (item) => { item.fieldsArray.forEach ( (item2, index) => { const newName = this.converUpperCamelCaseName(item2.name); data += `func (this *${item.name}) Get${newName}() ` + this.dumpClassField(item, item2, index) + ` {\n`; const newFieldName = this.keyWordReplace(item2.name); data += ` return this.${newFieldName}\n`; data += `}\n\n`; data += `func (this *${item.name}) Has${newName}() bool {\n`; if (item2.id < 64) { data += ` return (this._flags1_ & (uint64(1) << ${item2.id})) > 0\n`; } else { data += ` return (this._flags2_ & (uint64(1) << (${item2.id} - 64))) > 0\n`; } data += `}\n\n`; } ); } ); this.mtPb.nested.mt.nestedArray.forEach( (item) => { data += ` func (this *${item.name}) LoadFromKv(kv map[string]interface{}) { `; item.fieldsArray.forEach ( (item2, index) => { const newFieldName = this.keyWordReplace(item2.name); if (item2.id < 64) { data += ` f5.ReadMetaTableField(&this.${newFieldName}, "${item2.name}", &this._flags1_, ${item2.id}, kv)\n`; } else { data += ` f5.ReadMetaTableField(&this.${newFieldName}, "${item2.name}", &this._flags2_, ${item2.id} - 64, kv)\n`; } } ); data += '}\n'; } ); fs.writeFileSync('./mtb/mtb.auto_gen.go', data); } dumpClassField(cls, field, index) { const fieldName = field.name; switch (field.type) { case 'int32': { return `int32`; } break; case 'int64': { return `int64`; } break; case 'float': { return `float32`; } break; case 'double': { return `float64`; } break; case 'string': { return `string`; } break; default: console.log(field); assert(false, 'error field type:' + field.type); break; } } converUpperCamelCaseName(name) { let newName = ''; let preIs_ = false; for (let i = 0; i < name.length; ++i) { if (i == 0) { newName += name[i].toUpperCase(); preIs_ = name[i] == '_'; } else { if (preIs_) { if (name[i] != '_') { newName += name[i].toUpperCase(); } } else { if (name[i] != '_') { newName += name[i]; } } preIs_ = name[i] == '_'; } } return newName; } keyWordReplace(name) { const keyWords = { "type": 1 }; if (keyWords[name]) { return name + "_"; } else { return name; } } } (new PBTools).init();