# -*- coding: utf-8 -*- # # TECS Generator # Generator for TOPPERS Embedded Component System # # Copyright (C) 2008-2014 by TOPPERS Project #-- # 上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ # ア(本ソフトウェアを改変したものを含む.以下同じ)を使用・複製・改 # 変・再配布(以下,利用と呼ぶ)することを無償で許諾する. # (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作 # 権表示,この利用条件および下記の無保証規定が,そのままの形でソー # スコード中に含まれていること. # (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使 # 用できる形で再配布する場合には,再配布に伴うドキュメント(利用 # 者マニュアルなど)に,上記の著作権表示,この利用条件および下記 # の無保証規定を掲載すること. # (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使 # 用できない形で再配布する場合には,次のいずれかの条件を満たすこ # と. # (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著 # 作権表示,この利用条件および下記の無保証規定を掲載すること. # (b) 再配布の形態を,別に定める方法によって,TOPPERSプロジェクトに # 報告すること. # (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損 # 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること. # また,本ソフトウェアのユーザまたはエンドユーザからのいかなる理 # 由に基づく請求からも,上記著作権者およびTOPPERSプロジェクトを # 免責すること. # # 本ソフトウェアは,無保証で提供されているものである.上記著作権者お # よびTOPPERSプロジェクトは,本ソフトウェアに関して,特定の使用目的 # に対する適合性も含めて,いかなる保証も行わない.また,本ソフトウェ # アの利用により直接的または間接的に生じたいかなる損害に関しても,そ # の責任を負わない. # # $Id$ #++ #== GenOpaqueMarshaler # OpaqueRPCPlugin, sharedOpaqueRPCPlugin 共通の要素を集めたモジュール module GenOpaqueMarshaler # プラグイン引数名と Proc RPCPluginArgProc = { "clientChannelCelltype" => Proc.new { |obj,rhs| obj.set_clientChannelCelltype rhs }, "serverChannelCelltype" => Proc.new { |obj,rhs| obj.set_serverChannelCelltype rhs }, "clientChannelCell" => Proc.new { |obj,rhs| obj.set_clientChannelCell rhs }, "serverChannelCell" => Proc.new { |obj,rhs| obj.set_serverChannelCell rhs }, "clientChannelInitializer" => Proc.new { |obj,rhs| obj.set_clientChannelInitializer rhs }, "serverChannelInitializer" => Proc.new { |obj,rhs| obj.set_serverChannelInitializer rhs }, "clientSemaphoreCelltype" => Proc.new { |obj,rhs| obj.set_clientSemaphoreCelltype rhs }, "clientSemaphoreInitializer" => Proc.new { |obj,rhs| obj.set_clientSemaphoreInitializer rhs }, "clientErrorHandler" => Proc.new { |obj,rhs| obj.set_clientErrorHandler rhs }, "serverErrorHandler" => Proc.new { |obj,rhs| obj.set_serverErrorHandler rhs }, "TDRCelltype" => Proc.new { |obj,rhs| obj.set_TDRCelltype rhs }, "PPAllocatorSize" => Proc.new { |obj,rhs| obj.set_PPAllocatorSize rhs }, "substituteAllocator" => Proc.new { |obj,rhs| obj.set_substituteAllocator rhs }, "noServerChannelOpenerCode" => Proc.new { |obj,rhs| obj.set_noServerChannelOpenerCode rhs }, "taskCelltype" => Proc.new { |obj,rhs| obj.set_taskCelltype rhs }, "taskPriority" => Proc.new { |obj,rhs| obj.set_taskPriority rhs }, "stackSize" => Proc.new { |obj,rhs| obj.set_stackSize rhs }, } ##### プラグイン引数チェック関数 #=== プラグイン引数 taskPriority のチェック def set_taskPriority( rhs ) @taskPriority = rhs end #=== プラグイン引数 serverChannelCelltype のチェック def set_serverChannelCelltype( rhs ) @serverChannelCelltype = rhs.to_sym # path = [ "::", @serverChannelCelltype ] # obj = Namespace.find( path ) nsp = NamespacePath.analyze( @serverChannelCelltype.to_s ) obj = Namespace.find( nsp ) if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then cdl_error( "RPCPlugin: serverChannelCelltype '#{rhs}' not celltype or not defined" ) end end #=== プラグイン引数 clientChannelCelltype のチェック def set_clientChannelCelltype( rhs ) @clientChannelCelltype = rhs.to_sym # path = [ "::", @clientChannelCelltype ] # obj = Namespace.find( path ) nsp = NamespacePath.analyze( @clientChannelCelltype.to_s ) obj = Namespace.find( nsp ) if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then cdl_error( "RPCPlugin: clientChanneclCelltype '#{rhs}' not celltype or not defined" ) end end #=== プラグイン引数 serverChannelCell のチェック def set_serverChannelCell( rhs ) @serverChannelCell = rhs.to_sym # ChannelCell はプラグインで生成されるため、ここではチェックできない # path = [ "::", @serverChannelCell ] # obj = Namespace.find( path ) # if ! obj.instance_of?( Cell ) then # cdl_error( "RPCPlugin: serverChanneclCell '#{rhs}' not cell or not defined" ) # end end #=== プラグイン引数 clientChannelCell のチェック def set_clientChannelCell( rhs ) @clientChannelCell = rhs.to_sym # ChannelCell はプラグインで生成されるため、ここではチェックできない # path = [ "::", @clientChannelCell ] # obj = Namespace.find( path ) # if ! obj.instance_of?( Cell ) then # cdl_error( "RPCPlugin: clientChanneclCell '#{rhs}' not cell or not defined" ) # end end #=== プラグイン引数 serverChannelInitializer のチェック def set_serverChannelInitializer( rhs ) @serverChannelInitializer = rhs.to_sym end #=== プラグイン引数 clientChannelInitializer のチェック def set_clientChannelInitializer( rhs ) @clientChannelInitializer = rhs.to_sym end #=== タスクタイプ taskCellype のチェック def set_taskCelltype( rhs ) @taskCelltype = rhs.to_sym # path = [ "::", @taskCelltype ] # obj = Namespace.find( path ) nsp = NamespacePath.analyze( @taskCelltype.to_s ) obj = Namespace.find( nsp ) if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then cdl_error( "RPCPlugin: taskCelltype '#{rhs}' not celltype or not defined" ) end end #=== タスクタイプ stack\size のチェック def set_stackSize( rhs ) @stackSize = rhs end #=== プラグイン引数 PPAllocatorSize のチェック def set_PPAllocatorSize( rhs ) @PPAllocatorSize = rhs end #=== プラグイン引数 TDRCelltype のチェック def set_TDRCelltype( rhs ) @TDRCelltype = rhs.to_sym # path = [ "::", @TDRCelltype ] # obj = Namespace.find( path ) nsp = NamespacePath.analyze( @TDRCelltype.to_s ) obj = Namespace.find( nsp ) if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then cdl_error( "RPCPlugin: TDRCelltype '#{rhs}' not celltype or not found" ) end end #=== プラグイン引数 substituteAllocator のチェック # オプション引数が、以下の形式であることをチェック # substituteAllocator(Alloc.eAlloc=>Subst.eAlloc,Alloc2.eAlloc=>Subst2.eAlloc) def set_substituteAllocator( rhs ) #str::String : 破壊される(マッチした残りになる)。str.empty? で空になったことをチェックできる #regexp::Regexp : 期待するトークンにマッチする正規表現。 "\A" 出始める #expected::String: 期待するトークン、regexp が出現しなかった場合にエラーメッセージとして表示 def optparse (str,regexp,expected) str.strip! token = nil res = str.sub!( regexp ){ |matched| token = matched; "" } if ! token then cdl_error( "syntax error in substituteAllocator option near '#{str}', expected '#{expected}'" ) end return token end opt = rhs.dup ident_rexpr = /\A(\w[\w\d]*)/ # "Alloc.eAlloc=>CAlloc.eAlloc" の形式になっていることをチェック while true lhs_alloc_cell = optparse( opt, ident_rexpr, "allocator cell name" ) break if ! lhs_alloc_cell res = optparse( opt, /\A\./, "." ) break if ! res lhs_alloc_ent = optparse( opt, ident_rexpr, "allocator cell entry name" ) break if ! lhs_alloc_ent res = optparse( opt, /\A\=\>/, "=>" ) break if ! res rhs_alloc_cell = optparse( opt, ident_rexpr, "allocator cell name" ) break if ! rhs_alloc_cell res = optparse( opt, /\A\./, "." ) break if ! res rhs_alloc_ent = optparse( opt, ident_rexpr, "allocator cell entry name" ) break if ! rhs_alloc_ent # ここでは、右辺のチェックはできない。右辺のセルは前方参照となる # path = [ "::", rhs_alloc_cell.to_sym ] # mikan namespace # obj = Namespace.find( path ) # if ! obj.instance_of?( Cell ) || obj.get_region.get_path_string != @clientRegion then # cdl_error( "RPCPlugin: substituteAllocator: '#{rhs_alloc_cell}' not cell or not found in client region" ) # else # ct = obj.get_celltype # if ct # nil なら既にエラー # ent = ct.find rhs_alloc_ent # if ! ent.instance_of? Port || ent.get_port_type != :ENTRY || ent.get_signature == nil || ! ent.get_signature.is_allocator? # cdl_error( "RPCPlugin: substituteAllocator: '#{rhs_alloc_cell}.#{rhs_alloc_ent}' not entry port or not have alllocator signature" ) # end # end # end @substituteAllocator[ "#{lhs_alloc_cell}.#{lhs_alloc_ent}".to_sym ] = [ lhs_alloc_cell, lhs_alloc_ent, rhs_alloc_cell, rhs_alloc_ent ] # p "substituteAllocator: #{lhs_alloc_cell}.#{lhs_alloc_ent}=>#{rhs_alloc_cell}.#{rhs_alloc_ent}" break if opt.empty? res = optparse( opt, /\A\,/, "," ) break if ! res end end #=== プラグイン引数 noServerChannelOpenerCode のチェック def set_noServerChannelOpenerCode( rhs ) rhs = rhs.to_sym if rhs == :true @noServerChannelOpenerCode = true elsif rhs == :false then @noServerChannelOpenerCode = false else cdl_error( "RPCPlugin: specify true or false for noServerChannelOpenerCode" ) end end #=== プラグイン引数 clientSemaphoreCelltype のチェック def set_clientSemaphoreCelltype rhs @semaphoreCelltype = rhs.to_sym nsp = NamespacePath.analyze( @semaphoreCelltype.to_s ) obj = Namespace.find( nsp ) if ! obj.instance_of?( Celltype ) && ! obj.instance_of?( CompositeCelltype ) then cdl_error( "RPCPlugin: clientSemaphoreCelltype '#{rhs}' not celltype or not defined" ) end end #=== プラグイン引数 clientSemaphoreInitializer のチェック def set_clientSemaphoreInitializer rhs @semaphoreInitializer = rhs.to_sym end #=== プラグイン引数 clientErrorHandler のチェック def set_clientErrorHandler rhs @clientErrorHandler = rhs.to_sym end #=== プラグイン引数 serverErrorHandler のチェック def set_serverErrorHandler rhs @serverErrorHandler = rhs.to_sym end #=== セルの名前を得る # ThroughPlugin::get_cell_name plugin.rb をオーバーライド def get_cell_name @cell_name # @clientChannelCell end #=== marshaler のセルタイプ名を設定する def initialize_opaque_marshaler # オプション設定される変数のデフォルトを設定 @taskPriority = 11 @stackSize = 4096 @serverChannelCelltype = :"tSocketServer" @clientChannelCelltype = :"tSocketClient" @serverChannelCell = :"#{@cell_name}Server" @clientChannelCell = :"#{@cell_name}Client" @serverChannelInitializer = subst_name( "portNo=8931+$count$;" ).to_sym @clientChannelInitializer = subst_name( "portNo=8931+$count$; serverAddr=\"127.0.0.1\"; " ).to_sym @taskCelltype = :"tTask" @PPAllocatorSize = nil # @TDRCelltype = :"tTDR" # "tNBOTDR" に変更の予定 @TDRCelltype = :"tNBOTDR" @substituteAllocator = {} @noServerChannelOpenerCode = false @semaphoreCelltype = :"tSemaphore" @semaphoreInitializer = :"count = 1; attribute = C_EXP( \"TA_NULL\" ); "; @clientErrorHandler = nil @serverErrorHandler = nil @b_genOpener = false @taskMainCelltype = :"tRPCDedicatedTaskMain" @marshaler_celltype_name = :"tOpaqueMarshaler_#{@signature.get_global_name}" @unmarshaler_celltype_name = :"tOpaqueUnmarshaler_#{@signature.get_global_name}" @marshaler_celltype_file_name = "#{$gen}/#{@marshaler_celltype_name}.cdl" # signature で対応できないものをチェック @signature.each_param{ |func_decl, param_decl| if param_decl.get_direction == :OUT then if param_decl.get_count && ! param_decl.get_size then cdl_error( "#{@signature.get_namespace_path}.#{func_decl.get_name}.#{param_decl.get_name}: size_is must be specified for out parameter of Opaque RPC" ) end if param_decl.get_string == -1 then cdl_error( "#{@signature.get_namespace_path}.#{func_decl.get_name}.#{param_decl.get_name}: string length must be specified for out parameter of Opaque RPC" ) end end } end #=== GenOpaqueMarshaler# Opener Code の生成時のチェック def check_opener_code # サーバーチャンネルセルタイプが entry sServerChannelOpener eOpener を持つかどうかをチェック # mikan entry か (call でないか) をチェックしていない # scct = Namespace.find ["::", @serverChannelCelltype] # mikan namespace nsp = NamespacePath.analyze( @serverChannelCelltype.to_s ) scct = Namespace.find nsp if scct then obj = scct.find( :"eOpener" ) if obj.instance_of? Port then if obj.get_signature.get_name.to_sym == :sServerChannelOpener then if @noServerChannelOpenerCode == false then @b_genOpener = true @taskMainCelltype = :"tRPCDedicatedTaskMainWithOpener" end end end end if @noServerChannelOpenerCode == false && @taskMainCelltype != :"tRPCDedicatedTaskMainWithOpener" then cdl_warning( "O9999 ServerChannelOpener code not generated, not found 'entry sServerChannelOpener eOpener'") end end #=== GenOpaqueMarshaler# PPAllocator の必要性をチェックする def check_PPAllocator if @signature.need_PPAllocator?(true) then if @PPAllocatorSize == nil then cdl_error( "PPAllocatorSize must be speicified for size_is array" ) end end end ##### def gen_marshaler_celltype f = CFile.open( @marshaler_celltype_file_name, "w" ) # 同じ内容を二度書く可能性あり (AppFile は不可) if @PPAllocatorSize then alloc_call_port = " call sPPAllocator cPPAllocator;\n" else alloc_call_port = "" end f.print <\n\n" file.print "/* アンマーシャラ関数のプロトタイプ宣言 */\n" # signature に含まれる すべての関数について @signature.get_function_head_array.each { |f| f_name = f.get_name f_type = f.get_declarator.get_type id = @signature.get_id_from_func_name( f_name ) file.print "static ER tOpaqueUnmarshaler_#{@signature.get_global_name}_#{f_name}(CELLCB *p_cellcb, int16_t *state);\t/* func_id: #{id} */\n" } file.print "\n" end #=== POSTAMBLE 部のコード生成 # アンマーシャラセルタイプの場合、個々のアンマーシャラ関数の生成 def gen_postamble file, b_singleton, ct_name, global_name if ct_name != @unmarshaler_celltype_name.to_sym then return end file.print "\n/*** アンマーシャラ関数 ***/\n\n" @signature.get_function_head_array.each { |f| f_name = f.get_name f_type = f.get_declarator.get_type id = @signature.get_id_from_func_name( f_name ) # 関数は返り値を持つか? b_ret_er = false init_retval = "" if f_type.get_type.is_void? then b_void = true else b_void = false if f_type.get_type.get_type_str == "ER" || f_type.get_type.get_type_str == "ER_INT" then b_ret_er = true init_retval = " = E_OK" end end file.print < Decl-> FuncType->ParamList params.each{ |par| name = par.get_name type = par.get_type.get_original_type dir = par.get_direction if( dir == :RECEIVE )then # type は PtrType で、それを取り除いた型 type = type.get_type end if( dir == :SEND || dir == :RECEIVE )then init = " = 0" else init = "" end if type.kind_of? ArrayType then type = type.get_type aster = "(*" aster2 = ")" else aster = "" aster2 = "" end type_str = type.get_type_str.gsub( /\bconst\b */, "" ) # "const" を外す file.printf( " %-12s %s%s%s%s%s;\n", type_str, aster, name, aster2, type.get_type_str_post, init ) if dir == :OUT && type.is_nullable? then file.print( " int8_t\tb_#{name}_null_;\n" ) end } if ! b_void then file.printf( " %-12s retval_%s%s;\n", f_type.get_type.get_type_str, f_type.get_type.get_type_str_post, init_retval ) end # in 方向の入出力を入力 file.print "\n /* 入力引数受取 */\n" file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_RECV_BODY );\n" b_get = true # unmarshal では get b_marshal = false print_params( params, file, 1, b_marshal, b_get, true, "cServerCall", f_name ) print_params( params, file, 1, b_marshal, b_get, false, "cServerCall", f_name ) print_out_nullable( params, file, 1, b_marshal ); # パケットの受信完了 file.print " /* パケット終わりをチェック */\n" file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_RECV_EOP );\n" if ! f_type.is_oneway? then b_continue = "true" else b_continue = "false" end file.print " if( (ercd_=cTDR_receiveEOP(#{b_continue})) != E_OK )\n" file.print " goto error_reset;\n\n" # out のメモリをアロケート dir = :OUT; alloc_cp = "cPPAllocator_alloc"; alloc_cp_extra = nil; nest = 1 alloc_for_out_params( params, file, nest, dir, alloc_cp, alloc_cp_extra ) # 対象関数を呼出す file.print " /* 対象関数の呼出し */\n" file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_EXEC );\n" if b_void then file.print( " cServerCall_#{f_name}(" ) else file.print( " retval_ = cServerCall_#{f_name}(" ) end delim = " " params.each{ |par| file.print delim delim = ", " if par.get_direction == :RECEIVE then file.print "&" end file.print par.get_name } file.print( " );\n" ) # 戻り値、出力引数の受取コードの生成 # oneway の場合出力、戻り値が無く、受取を待たない(非同期な呼出し) if ! f.is_oneway? then file.print "\n /* SOPの送出 */\n" file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_SOP );\n" file.print " if( ( ercd_ = cTDR_sendSOP( false ) ) != E_OK )\n" file.print " goto error_reset;\n" b_get = false # unmarshaler は put if( ! b_void )then file.print " /* 戻り値の送出 */\n" print_param( "retval_", f_type.get_type, file, 1, :RETURN, nil, nil, b_marshal, b_get ) end if f_type.has_outward? then if b_ret_er then indent_level = 2 file.print " if( MERCD( retval_ ) != E_RPC ){\n" else indent_level = 1 end indent = " " * indent_level file.print "#{indent}/* 出力値の送出 */\n" file.print "#{indent}SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_BODY );\n" print_params( params, file, indent_level, b_marshal, b_get, true, "cServerCall", f_name ) print_params( params, file, indent_level, b_marshal, b_get, false, "cServerCall", f_name ) # receive のメモリをデアロケート if f_type.has_receive? then file.print "#{indent}/* dealloc receive parameter */\n" dir = :RECEIVE; dealloc_cp = "cServerCall_#{f_name}" dealloc_for_params( params, file, indent_level, dir, dealloc_cp ) end if b_ret_er then file.print " }\n" end end file.print " /* パケットの終わり(掃きだし) */\n" file.print " SET_RPC_STATE( *state_, RPCSTATE_SERVER_SEND_EOP );\n" file.print " if( (ercd_=cTDR_sendEOP(false)) != E_OK )\n" # b_continue = false file.print " goto error_reset;\n" end # ! f.is_oneway? file.print " return E_OK;\n" file.print < #{type.get_max} ){\t/* GenOpaqueMarshaler max check 2 */\n" file.print "#{indent} ercd_ = E_PAR;\n" file.print "#{indent} goto error_reset;\n" file.print "#{indent}}\n" end file.print <