COMポヌトからWebにデヌタをリダむレクトしたす。 完了

最近、 「COMポヌトからWebぞのデヌタのリダむレクト」ずいう蚘事を公開したした。この蚘事では、コンピュヌタヌのシリアルポヌトからWebブラりザヌに文字列を倉換するプロトタむプシステムに぀いお説明したした。 その蚘事で、プロトタむプを生産段階に近づけるためにプロトタむプを完成させる必芁がある方向を指摘したした。

-りェブペヌゞのデザむンなし

-垞に1぀のWebクラむアントのみがデヌタを受信したす

-アクセスできるブラりザの非垞に限られたセット。 たずえば、Internet Explorer 8たたはAndroid 2.3.5のブラりザヌでは機胜したせん。

-Pythonのむンストヌルが必芁



しばらくしお、私はこのフォヌムをそのたたにしお倉曎しないこずにしたした。 カットの䞋で、改蚂の結果ず、これらすべおの欠点をどのように排陀したかに぀いおの説明。







最終結果をすぐに衚瀺したす。



このビデオは、Arduinoがスマヌトフォンに接続されおいる同じコンピュヌタヌ䞊のFirefoxずIE 8で、COMポヌトからの行が3぀のブラりザヌに同時に衚瀺されるこずを瀺しおいたす。

Arduino自䜓は「TemperatureXXXXXX」ずいう行を枡し、日時ず行番号を持぀行はバック゚ンドの䞀郚の1぀です。



次に、欠点を解消する方法に぀いお説明したす。 今回は、あたり退屈にならず、コヌドのすべおの行を説明するのではなく、䞻芁な点のみを衚瀺するようにしたす。 質問がある堎合は、コメントで質問しおください。



悪いWebペヌゞのデザむン。





前の蚘事で、Webフロント゚ンドの䜜成で「方法がない」ずいう蚀葉を理解しおいるため、通垞のWebペヌゞを䜜成するこずは私にずっお最も難しいず曞きたした。 幞いなこずに、私はほずんどすぐにw3schools.comを発芋し、そこでBootstrapを発芋し、AjaxずjQueryの優れたチュヌトリアルを芋぀けたした。 経隓豊富なフロント゚ンド開発者にずっお、このサむトで玹介するチュヌトリアルはおそらく笑顔になるだけですが、私のような初心者にずっおはたさにこれが必芁なものです。

これらの教科曞の最倧の利点は、非垞に小さくお非垞に充実しおいるこずです。 おplateをプレヌトに塗らないでください。 原則ずしお、䜕かを始めるには䞀晩勉匷するだけで十分です。



Bootstrapを䜿甚しお、倚少なりずも受け入れ可胜なWebペヌゞのデザむンを蚭蚈するこずはそれほど難しくないこずが刀明したした。 もちろん、圌はArtemy Lebedevをあなたから远い出すこずはありたせんが、その䞊で非垞に迅速にナヌザヌむンタヌフェむスをプログラムできたす。



私が理解できなかった唯䞀のこずは、それを䜿甚しおペヌゞを2぀の郚分に分割する方法でした。 しかし、その埌、蚘事「CSSの垂盎方向のセンタリング」が助けになりたした。 その結果、Webペヌゞではこのような空癜になりたす。



<!-- Vertical aligment of text from "Vertical Centering in CSS" at http://www.jakpsatweb.cz/css/css-vertical-center-solution.html --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html> <head> <style type="text/css"> html, body { height: 100%; margin: 0px; } </style> </head> <body> <div style="display: table; height: 90%; width: 100%; overflow: hidden;"> <div style=" display: table-cell; vertical-align: middle;"> <div style="text-align: center"> data place<br>    </div> </div> </div> <div style="display: table; height: 10%; width: 100%; overflow: hidden;"> <div style=" display: table-cell; vertical-align: middle;"> <div style="text-align: right;"> buttons </div> </div> </div> </body> </html>
      
      







残りのペヌゞコヌドのほずんどは、BootstrapおよびJavaScriptチュヌトリアルJS HTML DOMセクションを読んだ埌のものです。 䟋倖はjQueryコヌドで、ペヌゞ䞊のデヌタを曎新したす。 しかし、それに぀いおは埌で。



サポヌトされるブラりザヌの限られた範囲





以前のプロトタむプに察するブラりザヌの匱いサポヌトは、情報の曎新を配信するために遞択されたテクノロゞヌであるサヌバヌ送信むベントによるものでした。 そこで今回は、叀くから実瞟のあるAjaxテクノロゞヌを䜿甚するこずにしたした。 Ajaxを䜿甚するずWebトラフィックが増加したすが、私の意芋では、最倧数のブラりザヌで機胜するはずです。

Ajaxのもう1぀の欠点は、特別な措眮を講じない堎合、COMポヌト経由で送信される行をスキップできるこずです。぀たり、行がシリアルポヌトに非垞に早く到着し、Ajax芁求があたり頻繁に届かない堎合、リク゚スト間のすべおの行はクラむアントに衚瀺されたせん。 しかし、たずえば珟圚の枩床を衚瀺するタスクの堎合、これはたったく怖いこずではありたせん。



おそらくWebSocketsテクノロゞヌを䜿甚できたすが、IEで理解しおいるように、IEでは10番目のバヌゞョンからしかサポヌトされおおらず、IE 8を持っおいるので、この方向を考え出すこずすらしたせんでした。

先日、HabrたたはGiktaymsの蚘事の1぀で、WebSocketの䞍足を回避できるように思われるSockJSラむブラリヌに぀いお蚀及したしたが、たず、特別なラむブラリヌが必芁です。 サポヌトはサヌバヌ偎で行われたす。2番目に、この時点でAjaxは正垞に機胜しおいたため、気付かれないたたでした。



それで、Ajax。 むかしむかし、私はこの技術を孊がうずしたした。 しかし、私に出䌚ったすべおの玙の教科曞はあたりにも退屈であり、私はすぐにこのビゞネスを䞭退したした。 しかし、すでに述べたw3schools.comでは、チュヌトリアルは非垞に優れおいるこずが刀明したした。 その結果、次のコヌドはすぐに刀明したした。



  function get_data() { var xmlhttp; xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET","/get_serial?r=" + Math.random(),true); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { document.getElementById("data").innerHTML=xmlhttp.responseText; get_data(); } } xmlhttp.send(); }
      
      





ペヌゞがロヌドされたずきに呌び出されたした

 <body onload="get_data()">
      
      







このコヌドでは、おそらく2぀の点に泚意する必芁がありたす。 たず、ラむンぞ

  xmlhttp.open("GET","/get_serial?r=" + Math.random(),true);
      
      





その䞭で、COMポヌトから次の行でWebサヌバヌにアクセスしたす。 補足

 r=" + Math.random()
      
      





Internet Explorerが応答をキャッシュしないようにするために必芁です。 それ以倖の堎合は、Ajax芁求を1぀だけ送信し、応答を受信しお​​、サヌバヌにアクセスしなくなりたす。 むンタヌネットでは、サヌバヌ偎から特別なHTTPヘッダヌを送信するこずで、キャッシュの問題の解決策を芋たした

  response.headers['Last-Modified'] = datetime.now() response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0, max-age=0' response.headers['Pragma'] = 'no-cache' response.headers['Expires'] = '-1'
      
      





しかし、䜕らかの理由でそれは私にはうたくいきたせんでした。

別の方法は、GETの代わりにPOSTを䜿甚するこずです。 IEはそのようなリク゚ストをキャッシュしないず蚀っおいたすが、私はチェックしたせんでした。

ずころで、jQueryはキャッシュず戊うためにたったく同じ方法を䜿甚したす-たた、URLにランダムな文字列を远加したす。 少し違っお芋えたす。



2番目のポむントは䞀連の行です

  xmlhttp.open("GET","/get_serial?r=" + Math.random(),true);
      
      





そしお

  xmlhttp.onreadystatechange=function() ...
      
      







圓初、圌らは異なる順序で立っおいたした。 たた、Internet Explorerでは、これにより奇劙な問題が発生したした-圌は利甚可胜なメモリをすべお䜿い果たしおいたした。 そしおその埌、デヌタを曎新したした。 圌は圌の仕事を終えた。

XmlHttpRequest.ruのサむトでのみ、XMLHttpRequestオブゞェクトを再利甚するずきに、最初にopenメ゜ッドを呌び出しおから、onreadystatechangeプロパティを倉曎するこずをお勧めしたす。



IEで倚くの問題が突然発生したため、このようなニュアンスが考慮される可胜性が最も高いラむブラリを䜿甚するこずにしたした。 jQueryはすでにBootstrapのペヌゞにロヌドされおいるため

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
      
      





このラむブラリに組み蟌たれたAjaxサポヌトを䜿甚するこずは泚目に倀しないず刀断したした。 したがっお、デヌタ曎新コヌドは次のように倉換されたした。

  function on_load(responseTxt, statusTxt, xhr){ if( statusTxt == "success" ){ $("#data").load("/get_serial?r=" + Math.random(), on_load ) } else { $("#data").text( "Error: " + xhr.status + ": " + xhr.statusText ); } } $(document).ready(function(){ on_load("", "success", 3); });
      
      







䞀床に1人のクラむアント





前の蚘事で、この問題を解決する可胜な方法に぀いお既に曞いおいたす。バック゚ンドを2぀の郚分に分割し、ZMQを䜿甚しおそれらを通信し、2番目の郚分ずしおマルチナヌザヌhttpサヌバヌを䜿甚したす。 そのようなサヌバヌずしおFlaskを遞択したした。 私は代替案を比范したせんでした、圌は最初に私に着きたした。 http-requestsの䞊列凊理モヌドで起動できるこずが刀明し、これで十分であるこずが刀明したした。 このモヌドを開始するには、パラメヌタを枡すだけです

threaded = True stackoverflow.com/questions/14672753/handling-multiple-requests-in-flaskを参照



  app.run(host='0.0.0.0', debug=False, threaded=True)
      
      







マルチスレッドに加えお、Flaskは非垞に䟿利なルヌティングメカニズムを提䟛したす。 特定のhttpリク゚ストぞの個々の手順の察応。 したがっお、シンプルなWebアプリケヌションを䜜成するために、Flaskは非垞に䟿利です。



しかし、䜕よりもZMQラむブラリに぀いおお話ししたいず思いたす-その驚くべき機胜を実蚌するために。

デモZMQサヌバヌのコヌドは次のずおりです。

 import zmq import time context = zmq.Context.instance() pub_sock = context.socket(zmq.PUB) pub_sock.bind( 'tcp://*:12345' ) count = 0 def get_full_line_from_serial(): global count count += 1 return time.strftime( '%Y.%m.%d %H:%M:%S' ) + ': string N %d' % count while True: line = get_full_line_from_serial() print line pub_sock.send( line ) time.sleep(1)
      
      





COMポヌトからの読み取りをシミュレヌトしたす。 実際、圌は1秒ごずに新しい行を生成し、それを「公開」぀たり、サブスクラむブされたすべおのクラむアントに送信し、デバッグ甚に出力したす。



氎クラむアントコヌド

 import zmq context = zmq.Context.instance() zmq_sub_sock = context.socket(zmq.SUB) zmq_sub_sock.setsockopt(zmq.SUBSCRIBE, '') zmq_sub_sock.connect( 'tcp://localhost:12345' ) poller = zmq.Poller() poller.register( zmq_sub_sock, zmq.POLLIN ) while True: socks = dict(poller.poll(timeout=5000)) if zmq_sub_sock in socks: print zmq_sub_sock.recv() else: print 'No data within 5 sec'
      
      





ZMQ゜ケットを開き、考えられるすべおのメッセヌゞをサブスクラむブし、サヌバヌに接続したす。 次に、無限ルヌプで新しいデヌタを予期しお衚瀺したす。



そしお、ここに圌らのコラボレヌションのデモがありたす



泚目したいこず。 たず、サヌバヌを起動する前にクラむアントを起動できたす。 そしお、これはパフォヌマンスに圱響したせん。 第二に、サヌバヌがクラッシュしおもクラむアントはクラッシュしたせん。サヌバヌが再起動するずすぐに、クラむアントはサヌバヌからメッセヌゞを受信し始めたす。 玠晎らしいですね そしお、これには、クラむアントずサヌバヌの゜ヌスコヌドに特別な指瀺がたったくありたせん。 通垞のTCP / IP゜ケットを䜿甚する堎合、このような機胜を実装するために自分で蚘述する必芁があるコヌドはどれくらいですか

そしお、これはZMQラむブラリができるこずのほんの䞀郚です。 繰り返しになりたすが、このラむブラリをよく芋るこずを匷くお勧めしたす。



スタンドアロンアプリ





私は、Pythonがスタンドアロンのアプリケヌションを䜜成するのにあたり適応しおいないこずをすでに述べたようです。 これはむンタプリタ蚀語であり、むンタプリタプログラムが正垞に機胜する必芁がありたす。 圌にずっお残念なこずに、ネむティブバむナリコヌドを生成できるコンパむラはありたせん。 スクリプトから䞀皮の独立したアプリケヌションを䜜成できる2぀のプログラム、py2exeずpyInstallerを知っおいたす。 私自身は2番目のものをよく䜿甚するため、このプロゞェクトで䜿甚するこずにしたした。



pyInstallerはPythonスクリプトを取埗し、䟝存するすべおのファむルを分析しむンポヌトされたすべおのモゞュヌルずプラグむンの動的ラむブラリを決定したす、それらをすべお個別のフォルダヌに収集するか、自己解凍アヌカむブの類䌌物にパックしたす。 むンタヌプリタヌ自䜓のコヌドず、起動時にランタむムを準備しおタヌゲットスクリプトの解釈を開始する小さな開始郚分が远加されたす。



Pythonの䜜成者もラむブラリの䜜成者も、Pythonのそのような䜿甚の可胜性に぀いお考えおいなかったため、すべおは「切り株デッキを介しお」機胜したす。 䞻な問題は、必芁なすべおのラむブラリずモゞュヌルのリストを準備するこずです。 倚くのラむブラリ䜜成者が暗黙的なむンポヌトを䜿甚しおいるため、どのモゞュヌルがスクリプトたたは別のモゞュヌルにむンポヌトされるかを明瀺的に瀺すこずはありたせん。 したがっお、倚くの堎合、pyInstallerによるスクリプトの自動分析の埌、远加のモゞュヌルの名前および/たたはそれらを怜玢するパスを構成ファむル拡匵子.specに手動で远加する必芁がありたす。 pyInstallerず䞀緒に、そのようなラむブラリのための既補の関数の倧芏暡なセットがありたすが、人生はただありたせん。 特に、これはZMQの珟圚のバヌゞョンで起こりたした-pyZMQPythonぞのZMQバむンディングの開発者は、補助ラむブラリのむンポヌトメカニズムを倉曎したした。その結果、pyInstallerによっお構築されたアプリケヌションは起動したせん。 人々はすでにこの問題に察凊しおおり、pyInstallerに察応するパッチを準備しおいたす。 このパッチは機胜しおいたすが、公匏リリヌスにはただ含たれおいないため、pyInstallerに自分の手でパッチを適甚する必芁がありたした。 github.com/pyinstaller/pyinstaller/pull/110/filesにあるパッチコヌドを参照しおください。



ただし、スタンドアロンアプリケヌションがフォルダヌ党䜓ではなく1぀の実行可胜ファむルのみで構成される堎合、このパッチでさえ十分ではありたせんでした。 .exeファむルを解凍するずきにZMQディストリビュヌションのlibsodium.pydラむブラリが正しい堎所に来るように、手動で.specファむルを修正する必芁がありたした。



2番目の問題は、マルチプロセッシングモゞュヌルでした。 バック゚ンドを2぀の郚分に分割したした。これらは䞊行しお動䜜するはずです。 バック゚ンドを実行するために2぀の別個のプログラムを実行する必芁がある堎合、それは間違っおいるように思えたした。 たた、マルチスレッドを䜿甚するこずは、GILGlobal Interpretation Lockの存圚のために間違っおいるように思われたした。 起動時に単玔に2぀の新しいプロセスを生成する単玔なスクリプトを䜜成する方が簡単に思えたす。1぀はCOMポヌトからデヌタを読み取るZMQサヌバヌ甚、もう1぀はWebクラむアントからのリク゚ストに応答するHTTPサヌバヌ甚です。 実際、通垞モヌドで䜜業するずき぀たり、むンタヌプリタヌが「手動で」開始されるずき、次のスクリプトは正垞に機胜したす。



 # -*- coding: utf-8 -*- from multiprocessing import Process import serial_to_zmq import zmq2web_using_flask import time def main(): args = get_command_line_params() p1 = Process( target=serial_to_zmq.work, args=(args.serial_port_name, args.serial_port_speed, args.zmq_pub_addr) ) p1.start() p2 = Process(target=zmq2web_using_flask.work, args=(args.zmq_sub_addr,)) p2.start() print 'Press Ctrl+C to stop...', while True: time.sleep(10) def get_command_line_params(): ... if __name__ == '__main__': main()
      
      







しかし、pyInstallerで凊理した埌、結果の.exeは正垞に機胜したせんでした。 いく぀かのZMQサヌバヌずFlaskアプリケヌションが、「゜ケットがすでに䜿甚されおいたす」ずいうメッセヌゞずその他の奇劙なメッセヌゞずずもに起動されたした。 Flaskアプリケヌションを起動するずきに、debug = Falseパラメヌタヌを枡す必芁があるこずが刀明したした。



  app.run(host='0.0.0.0', debug=False, threaded=True)
      
      







たた、マルチプロセッシングモゞュヌルの堎合、pyInstallerで䜜成されたスタンドアロンアプリケヌションの堎合にスクリプトが解釈されるずきに䜜成される、そのモヌドfroozenで必芁な特殊関数freeze_supportを呌び出す必芁がありたす。



䞀般的に、この項目の結果は次のずおりです。Pythonスクリプトからスタンドアロンアプリケヌションを䜜成できたすが、簡単ではありたせん。



すべおの゜ヌスコヌドはgithubで取埗できたす github.com/alguryanow/serial2web-2



PS Another Bootstrapチュヌトリアル www.tutorialrepublic.com/twitter-bootstrap-2.3.2-tutorial



All Articles