JP2016021157A - Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム - Google Patents
Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム Download PDFInfo
- Publication number
- JP2016021157A JP2016021157A JP2014144644A JP2014144644A JP2016021157A JP 2016021157 A JP2016021157 A JP 2016021157A JP 2014144644 A JP2014144644 A JP 2014144644A JP 2014144644 A JP2014144644 A JP 2014144644A JP 2016021157 A JP2016021157 A JP 2016021157A
- Authority
- JP
- Japan
- Prior art keywords
- data
- def
- cbt
- psid
- packet
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Pending
Links
Landscapes
- Accessory Devices And Overall Control Thereof (AREA)
Abstract
【課題】USB接続の一台の機器で複数の機能を持つことを可能にするプログラムを提供する。【解決手段】CPU101は主記憶装置102のROM1021あるいはRAM1022あるいは補助記憶装置105に格納されたプログラムに従って情報処理装置全体を制御する。RAM1022はCPU101が各種処理を行う際のワークエリアとしても使用される。補助記憶装置105はアプリケーション1051、プリンタードライバー1052、オペレーティングシステム(OS)1053等を記録する。出力I/F104は、データを外部に出力するためのインターフェースであり、モニタ1041やプリンター1042などの出力機器に対してデータを出力する。【選択図】図1
Description
本発明は、新しいアーキテクチャーのプリンタードライバーの、USB接続におけるIEEE1284.4−2000プロトコルを実現するためのプログラムに関する。
Microsoft(登録商標)のWindows8(登録商標)では、新しいアーキテクチャーのV4プリンタードライバーが導入された。V4プリンタードライバーでは、従来のプリンタードライバーにおいて、プリンターとの通信プロトコルの実装のために用いられていた、ランゲージモニターとポートモニターが廃止された。V4プリンタードライバーで使用できるのは、Windows(登録商標)に標準搭載されているプロトコルのポートモニターだけである。その代り、V4プリンタードライバーでは、ポートモニターにプリンターごとの機能情報を伝えるためのBidi Extensionとプロパティバッグが追加された。また、V4プリンタードライバーのUSB接続では、プリンターとのデータ通信をコントロールするためのUSB Bidi Extenderが追加された。USB BidiExtenderでは、Bidi XMLファイルとJavaScript(登録商標)を使用して、プリンターとのデータ通信を行う(非特許文献1)。
また、1台のプリンターで複数の機能を実現するためのプロトコルとして、IEEE1284.4−2000(以下、DOT4とする)がある。DOT4は、物理的に直接接続するピアツーピアの接続であるシリアルポートやパラレルポート、USBで使用されるもので、インターフェース上にチャネルを確立して、双方向通信を実現する(特許文献1)。Windows(登録商標)に標準搭載されているプロトコルにはDOT4がないので、従来はランゲージモニターやポートモニターをプリンターごとに独自に作成して、DOT4を実現していた。
Microsoft V4プリンタードライバー ホワイトペーパー
しかしながら、V4プリンタードライバーでは、標準搭載されているポートモニターしか使用できないので、USB接続においてDOT4のプロトコルを実現することができない。たとえば、USB接続のプリンターで、印刷データだけを送信する、もしくはプリンターからステータス情報を取得するといったように、片方だけの通信なら可能である。
しかし、DOT4のプロトコルを実現できないので、双方向によって複数の機能を同時に使用する動作、たとえば、印刷しながらプリンターの情報を取得することができなくなった。
本発明のUSBJavaScriptプログラムの構成は、
プロパティバッグにポートの情報とチャネルの情報とクレジットを保存する手段と、プロパティバッグからチャネルとクレジットを取得して、データを送受信したら更新する手段と、印刷ジョブを送信するときと構成情報を取得するときで重ならないように、プロパティバッグに別々の情報として保持しておく手段を持つことを特徴とする。
プロパティバッグにポートの情報とチャネルの情報とクレジットを保存する手段と、プロパティバッグからチャネルとクレジットを取得して、データを送受信したら更新する手段と、印刷ジョブを送信するときと構成情報を取得するときで重ならないように、プロパティバッグに別々の情報として保持しておく手段を持つことを特徴とする。
本発明により、USB接続のV4プリンタードライバーにおいてDOT4のプロトコルで通信できるので、印刷しながらステータス情報を取得するなど、複数の機能を同時に使用できるようになる。
以下、本発明を実施するための最良の形態について図面を用いて説明する。
図1は本発明の実施形態を示す一般的な情報処理装置(コンピューター)を用いたシステムのブロック構成図である。なお、特に断らない限り、本発明の機能が実行されるのであれば、単体の機能であっても、複数の機器からなるシステムであっても、ネットワークを介して接続がなされ処理が行われるシステムであっても、本発明を適用できることは言うまでもない。
CPU101は主記憶装置102のROM1021あるいはRAM1022あるいは補助記憶装置105に格納されたプログラムに従って情報処理装置全体を制御する。RAM1022はCPU101が各種処理を行う際のワークエリアとしても使用される。補助記憶装置105はアプリケーション1051、プリンタードライバー1052、オペレーティングシステム(OS)1053等を記録する。
キーボード1031やマウス・タッチパネルなどに代表されるポインティングデバイス1032などの入力機器は、入力I/F103を通じて、ユーザーがコンピューターに対して各種指示を与えるためのデバイスである。
出力I/F104は、データを外部に出力するためのインターフェースであり、モニタ1041やプリンター1042などの出力機器に対してデータを出力する。プリンター1042とは直接接続されるローカルI/Oのみならず、通信I/F106を通じて接続されるネットワーク1061を通して接続されていてもよい。
また、107は共通データシステムバスで、I/Fやモジュール間でデータのやりとりを行う。なお、本発明のフローチャートの各ステップは、CPUが、フローチャートに関するプログラムをメモリから読み出して実行することで実現される。
また、プリンター1042は、印刷機能のみを持った印刷装置でも良いし、FAX機能のみを持ったFAX装置でも良いし、スキャン機能および印刷機能など複数の機能を持った複合機でも良い。なお、プリンター1042を画像処理装置と呼ぶこともある。
図2は本発明の実施の形態におけるネットワーク1061の環境を簡略した図である。印刷される文書や画像を作成する単体もしくは複数のクライアントコンピュータ201/202がネットワークに接続されている。また、クライアントコンピュータのユーザーやプリンターを管理するサーバーコンピュータ203が接続されていることもある。単体もしくは複数のプリンター205および1042がネットワークに接続されている。
なお、プリンター205は、物理的にネットワークに接続されているが、実際には使用できないオフライン状態であることを示している。ネットワークにはPAN(Personal Area Network)、LAN(Local AreaNetwork)、MAN(Metropolitan Area Network)、WAN(Wide Area Network)などの小規模から大規模までのネットワークがあり、これらの機器がすべてのネットワークに接続されている。クラウドなど、サーバーやプリンターがインターネットを越えて接続されていてもかまわない。つまり、情報処理装置とは、他の装置(例えば、プリンター等の画像処理装置)と接続可能な装置である。
図3はV4プリンタードライバー印刷システムのブロック構成図である。V4プリンタードライバー印刷システムはXMLPaper Specification(以下、XPSと略す)と呼ばれるファイルフォーマットをスプールデータとして使用し、印刷を行うシステムである。図3におけるソフトウェアコンポーネントである1051、1052、1053は、補助記憶装置105に記憶されており、実行時にRAM1022にロードされ、CPU101によって実行される。V4プリンタードライバー印刷システムは、オペレーティングシステム1053上で動作している。
プリントマネージャ318、GDItoXPS変換モジュール307、フィルターパイプラインマネージャ312はオペレーティングシステム1053に含まれるモジュールである。GDItoXPS変換モジュール307、フィルターパイプラインマネージャ312は、プリンタードライバー1052に含まれているが、オペレーティングシステム1053から提供されているプリンタードライバー1052専用のモジュールである。
プリンタードライバー1052とフィルターパイプラインマネージャ312の各フィルターである313から315と、デバイスアプリ310は、図1の補助記憶装置105にプリンタードライバー1052として格納されている。なお、デバイスアプリ310は、プリンタードライバーのインストールに連動して、ネットワークを介して自動的にインストールされる。GDI印刷アプリケーション301(以下、GDIアプリと略す)とXPS印刷アプリケーション302(以下、XPSアプリと略す)は、図1の補助記憶装置105にアプリケーション1051として格納されている。
GDIアプリ301は、オペレーティングシステム1053が用意するGDI(Graphics Device Interface)を使用して印刷を行うアプリである。XPSアプリ302は、XPSスプールファイル306を直接使用して印刷を行うアプリである。ユーザーはキーボード1031やタッチパネル/マウス1032などといった入力装置を使用して、出力装置のモニタ1041に映し出されたGDIアプリ301、もしくはXPSアプリ302から印刷処理を指示する。なお、印刷処理は「プリンターの選択」、「印刷設定データの作成」、「描画データの変換」と3つの処理を順番に行うことで実現される。以下、印刷処理の流れを説明する。
まず、ユーザーは、印刷したいプリンター1042を選択する。ユーザーから見るとプリンター1042の選択は、印刷を実行するプリンター1042に対応したプリンタードライバー1052を選択することと同じである。
次に印刷設定データが作成される。印刷設定データが作成される場合、アプリケーション1051が印刷設定データ用のメモリ領域をRAM1022に確保する。そして、アプリケーション1051は、プリンタードライバー1052のコンフィグモジュール308を呼び出して、印刷設定データを作成して格納する。GDIアプリ301では印刷設定データとしてバイナリのDEVMODE構造体303を用い、XPSアプリ302ではマークアップ言語のXML(eXtensive MarkupLanguage)で記載されたプリントチケット304を用いる。
DEVMODE構造体303は、オペレーティングシステムが定義する標準領域とプリンタードライバーが独自で定義する拡張領域を持つ。プリントチケット304は、XML形式で記述された印刷設定データで、標準領域と拡張領域は名前空間によって記載が分かれている。印刷設定データには機種固有の情報も含まれているので、コンフィグモジュール308は、機種依存ファイル309を利用して、印刷設定データを作成する。
アプリケーション1051は、DEVMODE構造体303もしくはプリントチケット304の内容を書き換えることで印刷設定を変更する。印刷設定データには、出力する用紙サイズの情報(例えば「A4」)、両面印刷の指定、カラーと白黒、給紙段の指定など、印刷に必要な設定値が含まれる。印刷設定データの標準領域は仕様が外部に公開されているので、アプリケーションが直接変更できる。
プリンター1042に依存した拡張領域は、プリンタードライバー1052だけが詳細な仕様を把握しているので、通常はデバイスアプリ310が持つユーザーインターフェースを使ってユーザーが設定する。DEVMODE構造体303と異なり、プリントチケット304は設定値がXML形式で記述されているので、XPSアプリ302がすべての設定値を直接変更して書き換えることは可能ではある。しかし拡張領域は、プリンタードライバーごとの独自定義であるため詳細な仕様まではわからず、プリンター1042によって仕様が異なることから、デバイスアプリ310のユーザーインターフェースを使って設定値を変更するほうが容易である。
デバイスアプリ310は、プリンタードライバー1052の一種ではあるが、プリンタードライバーとは別のプログラムとしてインストールされる。デバイスアプリ310は、プリンタードライバーと、プリントチケット304を使用して印刷設定データをやりとりする。プリンタードライバー1052は、ユーザーインターフェースの設定に従い、DEVMODE構造体303もしくはプリントチケット304のプリンター1042に依存した設定を変更する。印刷設定データは文書を印刷するたびに必要となるので、印刷を実行するたびに作成される。
プリンター1042のオプション機器や、ユーザーごとの環境設定などは、プリンタードライバーがオペレーティングシステムのレジストリデータベース305、もしくはプロパティバッグ317に保存される。印刷設定データのデフォルト値はオペレーティングシステムのプリントマネージャ318が、レジストリデータベース305に保存する。レジストリデータベース305や、プロパティバッグ317は、補助記憶装置105に保存される。
最後に描画データの変換を行う。印刷設定データが確定したら、ユーザーはアプリケーションから印刷処理を指示する。GDIアプリ301から印刷する場合は、GDItoXPS変換モジュール307に描画データが送られ、XPSスプールファイル306が作成される。このときGDItoXPS変換モジュール307はコンフィグモジュール308を呼び出し、印刷設定データをDEVMODE構造体303からプリントチケット304に変換する。変換する際には、DEVMODE−プリントチケット変換モジュール3081が使用される。
DEVMODE−プリントチケット変換モジュール3081は、プロパティバッグ317の情報を読み書きできるので、デバイスアプリ310が保存したデータを参照して変換することもできる。XPSアプリ302から印刷する場合は、XPSスプールファイル306をXPSアプリ自身が生成するのと、オペレーティングシステムがXPSアプリからの描画命令に応じてXPSスプールファイル306を生成するのと2通りの方法がある。どちらの方法であっても、印刷の途中でXPSスプールファイル306が生成される。このようにV4プリンタードライバー印刷システムは、印刷時に必ず描画データに基づくXPSスプールファイル306を生成する点が特徴である。
XPSスプールファイル306が生成されたら、プリントフィルタパイプライン311に処理が渡される。プリントフィルタパイプライン311は複数のフィルターを通すことで印刷が行われる仕組みで、フィルタコンフィギュレーションファイル316がフィルターの数や順番を制御する。
プリントフィルタパイプライン311で動作するフィルターパイプラインマネージャ312はフィルタコンフィギュレーションファイル316に従って、この実施例では通信フィルター313、レイアウトフィルター314、レンダラーフィルター315の順に処理を行う。フィルターはプリンタードライバー1052の構成によって、数や種類が異なる。例えば、図3の3つのフィルターに加えて、更に、印刷ジョブを暗号化するための暗号化フィルターが含まれていても良い。
印刷処理はXPSスプールファイル306をフィルターに受け渡すことで行われ、フィルターがそれぞれXPSスプールファイル306を加工し次のフィルターに渡していくことで処理が進む。最終的にはXPSスプールファイル306が印刷ジョブに変換されて、プリンターに送信される。なお、プリンターは、印刷ジョブを解釈可能であり、送信された印刷ジョブを解釈することで印刷ジョブに基づく印刷処理を実行する。
それぞれのフィルターは独自のデータをプロパティバッグ317に保存することができる。また、それぞれのフィルターは、プロパティバッグ317から、オペレーティングシステム1053の情報や、他のフィルターのデータを取得することもできる。通信フィルター313では、印刷時に必要な情報をプリンターから取得し、プロパティバッグ317に保存しておく。
レイアウトフィルター314では倍率の変更や製本の面付けレイアウトやスタンプなどレイアウトに関する処理を行う。レイアウトフィルター314では、XPSスプールファイル306に含まれるプリントチケット304に従って動作する。そのため、たとえば面付けの設定がプリントチケット304に存在しないときは、レイアウトフィルター314は何も動作せずスルーして、次のフィルターにXPSスプールファイル306をそのまま渡す。
レンダラーフィルター315は、入力されたXPSスプールファイル306をレンダリングしてページ記述言語(以下、PDL(PageDescription Language)と略す)に変換する。また、レンダラーフィルター315は、プリントチケット304の印刷設定データのうち標準領域の設定を、プリンタジョブ言語(以下、PJL(Printer JobLanguage)と略する)に変換する。レンダラーフィルター315は、PDLとPJLをまとめて印刷ジョブとして、次のフィルターにストリームデータとして渡す。レンダラーフィルター315は、変換したPJLと渡されたPDLとPJLをまとめて印刷ジョブとして、フィルターパイプラインマネージャ312に渡す。
印刷ジョブは、印刷処理のスケジュール管理を行うプリントマネージャ318に送られ、キュー(待ち行列)に印刷ジョブが次々と登録される。プリントマネージャ318は、プリンター1042と通信してプリンター1042が印刷できる状態になり次第、キューに登録した順にポートモニター319を通して印刷ジョブを送信する。
このようにして、描画データをおよび印刷設定データを印刷ジョブに変換することがプリンタードライバーの主な役目であり、情報処理装置での印刷が実施される。
なお、本実施例では、印刷処理を用いて説明しているためプリンタードライバーと記載しているが、FAX送信処理の際にも本発明を適用できる。この場合は、レンダラーフィルター315の代わりにFAXデータを作成するFAXフィルターを搭載して、FAX機器に送信するFAXドライバーとなる。
図4は、ポートモニター319のうち、USB接続時のポートモニターを表した図である。USB BidiExtender401と、USBポートモニター402の2つによって、USB接続時のポートモニターの機能が実現されている。USB BidiExtender401は、JavaScriptのプログラム4011と、Bidi XMLファイル4012の2つからなる。USB BidiExtender401が呼び出されるのは、フィルターによって生成された印刷ジョブがプリントマネージャ318を通してプリンター1042に送信されるときと、デバイスアプリ310などからプリントマネージャ318を通してプリンター1042から情報を取得するときである。印刷ジョブが送信されるときは、V4プリンタードライバー1052で“HostBasedDevice”のフラグを立てることで、JavaScriptのプログラム4011にある以下の3つのインターフェースが呼び出される。
startPrintJob()
writePrintData()
endPrintJob()
また、プリンター1042から情報を取得するときは、アプリケーションが、Bidiインターフェースを使って、BidiXMLファイル4012に記載されたスキーマに基づき、JavaScriptのプログラム4011にある以下のインターフェースが呼び出される。
writePrintData()
endPrintJob()
また、プリンター1042から情報を取得するときは、アプリケーションが、Bidiインターフェースを使って、BidiXMLファイル4012に記載されたスキーマに基づき、JavaScriptのプログラム4011にある以下のインターフェースが呼び出される。
getSchemas()
JavaScriptのプログラム4011は、呼び出されたインターフェースを通じて、USBポートモニター402に命令を送り、USBPRINTカーネルドライバー403を使ってプリンター1042と通信する。このように、V4プリンタードライバー1052は、USB BidiExtender401を通じて、プリンター1042と双方向のデータをやりとりする通信を行う。
JavaScriptのプログラム4011は、呼び出されたインターフェースを通じて、USBポートモニター402に命令を送り、USBPRINTカーネルドライバー403を使ってプリンター1042と通信する。このように、V4プリンタードライバー1052は、USB BidiExtender401を通じて、プリンター1042と双方向のデータをやりとりする通信を行う。
V4プリンタードライバー1052のUSB Bidi Extender401を使用したUSB通信の転送モードは、大容量データの一括転送を行うバルク転送モードしか使用できない。プリンター1042のステータス情報や設定値などは、半二重通信が可能なコントロール転送モードを使用して通信したいが、USB BidiExtender401では、エンドポイント0のコントロール転送モードはサポートされていない。したがって、巨大なPDLのデータを送信する印刷を行いながら、プリンター1042のステータス情報を取得するためのコマンドを発行することはできず、印刷データの転送が終了するまで、ステータス情報を取得できなくなっている。また、複数のアプリやドライバーがBidiインターフェースを同時に使用すると、印刷データの途中にステータス情報コマンドが挿入されてしまい、プリンター1042がエラーになってしまう。そこで、DOT4のチャネルをJavaScriptのプログラム4011で確立して、双方向で同時に通信する手段を実現する。
図5はDOT4のパケットを示したものである。DOT4では、送信側機器をPrimarySocketID(PSID)というSocketIDで示し、受信側機器をSecondarySocketID(SSID)というSocketIDで示す。パケットは、基本的には6バイトのヘッダ部と、ヘッダ部に続くデータ部から構成されている。ヘッダ部は、先頭から、チャネルをオープンした側を認識するPSID、オープンされたチャネルの相手先を認識するSSID、パケットのデータサイズ(バイト数)を示す長さ(Length)、相手に渡すクレジット数コントロール(ノーマルパケットは「0」)を示すクレジット部、当該パケットのコマンドを示す制御部(Control)より構成される。
それぞれのSocketIDを接続したデータの経路がチャネルとなる。チャネルは複数確立することもでき、それぞれで異なるデータを送受信することができる。本実施例では、コマンド用、ジョブ用、管理用という3つのチャネルを確立する。チャネルの通信路上には、パケットを1つのまとまりとして送受信する。チャネルを分離し、パケット単位でデータのやりとりをすることで、同時に複数の機能の双方向通信を行う。
また、DOT4ではCreditBasedTrasnfer(CBT)と呼ばれるクレジットベースの転送方式が用いられる。通常の転送方式では、データを受け取る受信側機器が持つ受信バッファのデータがあふれたら、送信側機器に対してフロー信号を送信して、一時的に送信を停止する。その後、受信バッファがクリアされたら、再度送信を開始することで、通信を途切れないように行う。この方式は、フロー信号のやりとりが発生し、パフォーマンスが落ちることと、フロー信号が間に合わずデータの取りこぼしが発生する可能性がある。
クレジットベースの転送方式では、送信側機器は、まず受信側機器に送信できるデータのサイズを問い合わせて、データ量に対応したクレジットと呼ばれる権利を受け取る。受信したクレジットに対するサイズについては送信できることが保障されているので、データの送信を行い、クレジットがなくなったら再度クレジットを要求する。このクレジット方式では、送信側から制御しているので、安定した通信が可能になる。
以下、印刷ジョブを送信するための開始処理からシーケンスを説明する。全体のシーケンスは図5にある。なお、本実施例のプログラム上では、エラー処理をほとんど記載していないが、実際は、すべてのメソッドをコールするたびにエラー処理を行う必要がある。また、変数定義も記述スペースの都合により省略しているが、実際には正しく定義する必要がある。
USBポートモニター402は、フィルターによって生成された印刷ジョブが、プリントマネージャ318を通じて到達すると、JavaScriptのプログラム4011のstartPrintJob()を呼び出す。startPrintJobでは、CreditBasedTransferManager(CBTMgr)のOpenを呼び出して、初期化を行う。成功したら、プロパティバッグ317にPSIDを詰めておく。
function startPrintJob(usbJobContext, printerStream, printerBidiSchemaResponses)
{
ret = cbtMgr.Open(cbtDef.CBT_JOB_SSID);
if (ret.ret === cbtDef.CBT_SUCCESS) {
properties = cbtMgr.GetProperty();
properties.openedPsid = ret.psid;
usbJobContext.jobPropertyBag.SetString("Dot4Cache", json.stringify(properties));
}
}
CBTMgrのOpenでは、Initを呼び出してDOT4の初期化を行った後、データを送信するために、OpenChannelでチャネルをオープンする。チャネルのオープンに成功したら、クレジットとともにキャッシュ用バッファの最大サイズをSSIDから取得するので、保存しておく。
that.Open = function () {
ret = my.CbtInit();
ret1 = my.CbtOpenChannel(ssid);
if ((ret1.ret === my.def.CBT_SUCCESS) {
ret2 = my.m_CbtChannelList.GetPktSizeFromChannel(ret1.psid);
my.m_JobCacheSize = ret2.priToSecPktSize - my.def.CBT_HEADER_SIZE; my.m_JobCacheDataSize = 0;
}
}
Initでは実際にプリンター1042に対してInitコマンドを送信し、プリンター1042がDOT4モードに移行できるようにする。Initコマンドは、コマンドチャネルを使って送信するので、チャネルを作成する際に、SSIDにコマンドチャネル(CBT_CMD_SSID)を指定する。通常、DOT4のチャネルを使用するには、OpenChannelコマンドを発行する必要があるが、コマンドチャネルは特別なチャネルで、発行しなくても使用することができる。Initコマンドを送信し終わったら、コマンドチャネルを閉じる。
my.CbtInit = function () {
ret = my.m_CbtChannelList.CreateChannel(my.def.CBT_CMD_SSID);
my.m_CbtChannelList.MngCredit(ret.psid, my.def.CBT_READ_DIRECTION, my.CBT_START_CMD_CREDIT);
ret2 = my.m_CbtPkt.CmdInit();
if (ret2.ret !== my.def.CBT_REPLY_SUCCESS) {
my.m_CbtChannelList.DestroyChannel(ret.psid);
}
}
CmdInitでは、実際にパケットを生成してInitコマンドを送信する。パケットは図5のDOT4の構成にした構造体を元にエンコードすることで作成し、データを送信する。応答のパケット(InitReply)が返ってくるまでWaitで待った後、受信したパケットのデータをデコードすることで、プリンター1042が返してきたデータを取得する。
that.CmdInit = function () {
param = {
header: {
psid: my.def.CBT_CMD_PSID,
ssid: my.def.CBT_CMD_SSID,
length: my.CBT_CMD_INIT_SIZE,
credit: 1,
control: my.CBT_CONTROL_CMD
},
data: {
command: my.def.IEEE1284_4_CMD_INIT,
revision: revision
}
}
cmdPkt = my.m_CbtCodec.Encode(param);
my.SendPkt(my.def.CBT_CMD_PSID, cmdPkt);
ret = my.WaitReplyPkt(my.def.CBT_CMD_PSID, my.def.IEEE1284_4_CMD_INIT);
data = my.m_CbtCodec.DecodeData(ret.packet.slice(my.def.CBT_HEADER_SIZE));
}
Encodeでは、入力された構造体にしたがって、パケットを生成する。DOT4のコマンドの場合は、それぞれ専用のメソッドでパケットを生成する。コマンド以外の場合は、データとしてパケットを作成する。パケットの作成の仕方は、1byteもしくは数byteずつ値を詰めていく方式をとっており、EncodeByteなどの関数で詰めていく。
CNBIDI.cbt.std.Codec.Encode = function (param) {
var packet = [];
my.codec.EncodeByte(param.header.psid, packet);
my.codec.EncodeByte(param.header.ssid, packet);
my.codec.EncodeShort(param.header.length, packet);
my.codec.EncodeByte(param.header.credit, packet);
my.codec.EncodeByte(param.header.control, packet);
if ((param.header.psid === my.def.CBT_CMD_PSID) && (param.header.ssid === my.def.CBT_CMD_SSID)) {
// command packet
my.codec.EncodeByte(param.data.command, packet);
switch (param.data.command) {
case my.def.IEEE1284_4_CMD_INIT:
my.EncodeCmdInit(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_OPENCHANNEL:
my.EncodeCmdOpenChannel(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CLOSECHANNEL:
my.EncodeCmdCloseChannel(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CREDIT:
my.EncodeCmdCredit(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CREDITREQUEST:
my.EncodeCmdCreditRequest(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_EXIT:
break;
case my.def.IEEE1284_4_CMD_GET_SOCKETID:
break;
//case IEEE1284_4_CMD_GET_SERVICENAME:
// break;
case my.def.IEEE1284_4_CMD_ERROR:
my.EncodeCmdError(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_INIT_REPLY:
my.EncodeCmdInitReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_OPENCHANNEL_REPLY:
my.EncodeCmdOpenChannelReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CLOSECHANNEL_REPLY:
my.EncodeCmdCloseChannelReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CREDIT_REPLY:
my.EncodeCmdCreditReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_CREDITREQUEST_REPLY:
my.EncodeCmdCreditRequestReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_EXIT_REPLY:
my.EncodeCmdExitReply(param.data, packet);
break;
case my.def.IEEE1284_4_CMD_GET_SOCKETID_REPLY:
break;
// case my.def.IEEE1284_4_CMD_GET_SERVICENAME_REPLY:
// break;
default:
break;
}
} else {
// data packet
my.codec.EncodeBinary(param.data, Math.min(param.header.length - my.def.CBT_HEADER_SIZE, param.data.length), packet);
}
return packet;
}
return that;
}
that.EncodeByte = function (data, packet) {
packet.push(data);
}
SendPktではエンコードで作成したパケットを、送信する。まず、write用のクレジットの数を確認して、クレジットがなければ送信しない。つぎに、USBポート402に対して、パケットを送信する。パケットにはSSIDに対してread用のクレジットを付与して送信する。パケットを送信したら、read用のクレジットは付与した分だけ追加しwrite用のクレジットは1つ減らす。
CNBIDI.cbt.cn.Pkt.SendPkt = function (psid, packet) {
var ret,
header;
ret = my.m_CbtChannelList.GetNumCredit(psid, my.def.CBT_WRITE_DIRECTION);
if (ret.credit === 0) {
return my.def.CBT_ERROR_NO_CREDIT;
}
if (!my.m_UsbIo.Send(packet)) {
return my.def.CBT_ERROR_WRITE_FAILD;
}
my.m_CbtChannelList.MngCredit(psid, my.def.CBT_READ_DIRECTION, header.credit);
my.m_CbtChannelList.MngCredit(psid, my.def.CBT_WRITE_DIRECTION, -1);
return my.def.CBT_SUCCESS;
}
USBポート402に対してのSendは、プリンターストリームにデータを書き込むことで行われる。
UsbIo.Send = function (sendBuff) {
var result = false,
totalWrittenSize = 0,
time = Time(),
writtenSize;
while ((totalWrittenSize < sendBuff.length) && (time.GetTime() < m_WriteTimeout)) {
try {
writtenSize = printerStream.Write(sendBuff.slice(totalWrittenSize));
} catch (e) {
}
totalWrittenSize += writtenSize;
if (totalWrittenSize === sendBuff.length) {
result = true;
break;
} else if (totalWrittenSize === 0) {
break;
}
return result;
}
WaitReplyPktは、応答のパケットが返ってくるまで待つ。タイムアウトするか、規定回数パケットが取得できるまでリトライを行う。
my.WaitReplyPkt = function (psid, cmdId) {
while ((time.GetTime() < timeout) || (strike < my.STRIKEOUT)) {
ret1 = that.RecvPkt(psid);
if (ret1 < 0) {
if (ret1 === my.def.CBT_ERROR_NO_PKT) {
strike += 1;
}
else if (ret1 === my.def.CBT_ERROR_INVALID_PARAM) {
return { ret: my.def.CBT_ERROR_FATAL, packet: packet };
}
else {
return { ret: ret1, packet: packet };
}
}
ret2 = my.m_CbtChannelList.GetNumPktInChannel(psid);
if (ret2.numPkt < 0) {
return { ret: my.def.CBT_ERROR_FATAL, packet: packet };
}
found = false;
for (i = ret2.numPkt; i > 0; i--) {
ret3 = my.m_CbtChannelList.GetPktFromChannel(psid);
if (ret3.ret >= 0) {
data = my.m_CbtCodec.DecodeData(ret3.packet.slice(my.def.CBT_HEADER_SIZE));
if (data.command & 0x80) {
if (data.command === (cmdId | 0x80)) {
found = true;
packet = ret3.packet;
break;
}
}
}
}
if (found) {
return { ret: my.def.CBT_SUCCESS, packet: packet };
}
if (ret1 === 0) {
time.Start();
}
}
return { ret: my.def.CBT_ERROR_TIMEOUT, packet: packet };
}
RecvPktではポートから1パケット取得して、パケットに含まれているwriteクレジットを更新し、データをデコードする。readクレジットは読み込んだので1つ減らす。コマンドパケットだったら、それぞれのコマンドパケットを読み込む。
that.RecvPkt = function (psid) {
ret1 = my.Recv1PktFromPort();
header = my.m_CbtCodec.DecodeHeader(ret1.packet.slice(0, my.def.CBT_HEADER_SIZE));
my.m_CbtChannelList.MngCredit(header.psid, my.def.CBT_WRITE_DIRECTION, header.credit);
my.m_CbtChannelList.MngCredit(header.psid, my.def.CBT_READ_DIRECTION, -1);
if ((header.psid === my.def.CBT_CMD_PSID) && (header.ssid === my.def.CBT_CMD_SSID)) {
data = my.m_CbtCodec.DecodeData(ret1.packet.slice(my.def.CBT_HEADER_SIZE));
if (!(data.command & 0x80)) {
ret2 = my.RecvCmdPkt(ret1.packet);
if ((ret2 === my.def.CBT_SUCCESS)
return my.def.CBT_SUCCESS;
}
}
ret3 = my.m_CbtChannelList.PutPktToChannel(header.psid, ret1.packet);
desiredDataLength = 0;
if (psid === header.psid) {
desiredDataLength = header.length;
}
return desiredDataLength;
}
Recv1PktFromPortでは、ポートから1パケット取得するために、ヘッダーサイズと、データサイズをストリームから読み込む。
my.Recv1PktFromPort = function () {
packet = my.m_UsbIo.Recv(my.def.CBT_HEADER_SIZE);
header = my.m_CbtCodec.DecodeHeader(packet.slice(0, my.def.CBT_HEADER_SIZE));
if (header.length > my.def.CBT_HEADER_SIZE) {
Array.prototype.push.apply(packet, my.m_UsbIo.Recv(header.length - my.def.CBT_HEADER_SIZE));
}
return { ret: my.def.CBT_SUCCESS, packet: packet };
}
USBポート402に対してのRecvは、プリンターストリームから読み込むことで行われる。
that.Recv = function (size) {
reqSize = size - totalReadBuff.length;
readSize = 0;
do {
isReadSuccess = false;
readBuff = [];
if (readSize > 0) {
readBuff = printerStream.Read(readSize);
if (readBuff.length > 0) {
isReadSuccess = true;
}
}
if (isReadSuccess) {
Array.prototype.push.apply(totalReadBuff, GetCacheData(reqSize));
Array.prototype.push.apply(totalReadBuff, readBuff.splice(0, Math.min(size - totalReadBuff.length, readBuff.length)));
SetCacheData(readBuff);
}
} while (isReadSuccess &&
(size > totalReadBuff.length) &&
(totalReadBuff.length > 0) &&
(time.GetTime() < m_ReadTimeout));
return totalReadBuff;
}
RecvCmdPktはコマンドパケットを取得して、コマンドに従った処理を実行する。クレジットコマンドであれば、SSIDから受け取ったwriteのクレジットを増やし、クレジットリクエストコマンドであれば、SSIDにreadのクレジットを渡す。
my.RecvCmdPkt = function (packet) {
header = my.m_CbtCodec.DecodeHeader(packet.slice(0, my.def.CBT_HEADER_SIZE));
if ((header.psid !== my.def.CBT_CMD_PSID) || (header.ssid !== my.def.CBT_CMD_SSID)) {
return my.def.CBT_SUCCESS;
}
data = my.m_CbtCodec.DecodeData(packet.slice(my.def.CBT_HEADER_SIZE));
switch (data.command) {
case my.def.IEEE1284_4_CMD_CREDIT:
my.m_CbtChannelList.MngCredit(data.psid, my.def.CBT_WRITE_DIRECTION, data.credit);
my.CmdCreditReply(result, data.psid, data.ssid);
break;
case my.def.IEEE1284_4_CMD_CREDITREQUEST:
creditReq = data.creditRequested;
ret = my.m_CbtChannelList.GetNumPktInChannel(data.psid);
if (ret.numPkt >= 0) {
maxPkt = ret.maxPkt;
numPkt = ret.numPkt;
ret = my.m_CbtChannelList.GetNumCredit(data.psid, my.def.CBT_READ_DIRECTION);
if (ret.ret === my.def.CBT_SUCCESS) {
remainCredit = maxPkt - numPkt - ret.curCredit;
if (remainCredit > 0) {
creditGra = Math.min(creditReq, remainCredit);
}
}
}
my.CmdCreditRequestReply(result, data.psid, data.ssid, creditGra);
my.m_CbtChannelList.MngCredit(data.psid, my.def.CBT_READ_DIRECTION, creditGra);
break;
case my.def.IEEE1284_4_CMD_INIT_REPLY:
case my.def.IEEE1284_4_CMD_OPENCHANNEL_REPLY:
case my.def.IEEE1284_4_CMD_CLOSECHANNEL_REPLY:
case my.def.IEEE1284_4_CMD_CREDIT_REPLY:
case my.def.IEEE1284_4_CMD_CREDITREQUEST_REPLY:
case my.def.IEEE1284_4_CMD_GET_SOCKETID_REPLY:
my.m_CbtChannelList.PutPktToChannel(header.psid, packet);
break;
case my.def.IEEE1284_4_CMD_ERROR:
my.m_CbtChannelList.MngCredit(header.psid, my.def.CBT_READ_DIRECTION, 1);
my.m_CbtChannelList.MngCredit(header.psid, my.def.CBT_WRITE_DIRECTION, 2);
my.WaitExitCmd();
return my.def.CBT_ERROR_EXIT_DOT4;
break;
case my.def.IEEE1284_4_CMD_EXIT:
my.CmdExitReply();
return my.def.CBT_ERROR_EXIT_DOT4;
break;
case my.def.IEEE1284_4_CMD_INIT:
my.CmdInitReply(my.def.CBT_REPLY_ERR_NOT_AVAILABLE, 0x10);
break;
case my.def.IEEE1284_4_CMD_OPENCHANNEL:
my.CmdOpenChannelReply(my.def.CBT_REPLY_ERR_NOT_AVAILABLE, data.psid, data.ssid, 0, 0, 0, 0);
break;
case my.def.IEEE1284_4_CMD_CLOSECHANNEL:
my.CmdCloseChannelReply(my.def.CBT_REPLY_ERR_NOT_AVAILABLE, data.psid, data.ssid);
break;
}
return my.def.CBT_SUCCESS;
}
Initコマンドを送信すると、InitReplyが返答として返ってくるので、PutPktToChannelでパケットを読み込む。
that.PutPktToChannel = function (packet) {
header = m_CbtCodec.DecodeHeader(packet.slice(0, def.CBT_HEADER_SIZE));
if (m_ReadNumPkt === m_ReadMaxPkt) {
ret = that.DeleteOldPacket();
}
Array.prototype.push.apply(m_ReadBuffer, packet.slice(0, header.length));
m_ReadNumPkt += 1;
return header.length;
}
このようにして、Initコマンドは送信される。次に、OpenChannelが行われ、印刷ジョブデータの送受信を行うためのチャネルを確立する。これはCbtOpenChannelで行われる。CbtOpenChannelでは、実際にCmdOpenChannelを呼び出してチャネルを確立するのと同時に、どのくらいのチャネルが確立されているかを管理するリストに対して、追加する。
my.CbtOpenChannel = function (ssid) {
switch (ssid) {
case my.def.CBT_JOB_SSID:
priToSecPktSize = my.CBT_JOB_PKTSIZE;
secToPriPktSize = my.CBT_JOB_PKTSIZE;
creditRequested = my.CBT_JOB_CREDITREQUEST;
maxOutstandingCredit = my.CBT_JOB_MAX_CREDIT;
break;
case my.def.CBT_ADMIN_SSID:
priToSecPktSize = my.CBT_ADMIN_PKTSIZE;
secToPriPktSize = my.CBT_ADMIN_PKTSIZE;
creditRequested = my.CBT_ADMIN_CREDITREQUEST;
maxOutstandingCredit = my.CBT_ADMIN_MAX_CREDIT;
break;
case my.def.CBT_RESOURCE_SSID:
priToSecPktSize = my.CBT_RESOURCE_PKTSIZE;
secToPriPktSize = my.CBT_RESOURCE_PKTSIZE;
creditRequested = my.CBT_RESOURCE_CREDITREQUEST;
maxOutstandingCredit = my.CBT_RESOURCE_MAX_CREDIT;
break;
default:
return { ret: my.def.CBT_ERROR_INVALID_PARAM, psid: psid };
break;
}
credit = creditRequested;
ret1 = my.m_CbtChannelList.CreateChannel(ssid);
ret2 = my.m_CbtPkt.CmdOpenChannel(ret1.psid, ssid, priToSecPktSize, secToPriPktSize, creditRequested, maxOutstandingCredit);
ret3 = my.m_CbtChannelList.OpenChannel(ret1.psid, ret2.priToSecPktSize, ret2.secToPriPktSize, credit, ret2.maxOutstandingCredit, ret2.credit);
return { ret: my.def.CBT_SUCCESS, psid: ret1.psid };
}
CmdOpenChannelではOpenChannelのコマンドパケットを生成して、エンコードし、パケットを送信する。応答(OpenChannelReply)を待って、デコードして反映させる。OpenChannelReplyでは、目的(ここでは印刷ジョブデータの送信)に応じて、SSIDからクレジットが発行される。
that.CmdOpenChannel = function (psid, ssid, priToSecPktSize, secToPriPktSize, creditRequested, maxOutstandingCredit) {
param = {
header: {
psid: my.def.CBT_CMD_PSID,
ssid: my.def.CBT_CMD_SSID,
length: my.CBT_CMD_OPENCHANNEL_SIZE_STD,
credit: 1,
control: my.CBT_CONTROL_CMD
},
data: {
command: my.def.IEEE1284_4_CMD_OPENCHANNEL,
psid: psid,
ssid: ssid,
priToSecPktSize: priToSecPktSize,
secToPriPktSize: secToPriPktSize,
maxOutstandingCredit: maxOutstandingCredit
}
}
cmdPkt = my.m_CbtCodec.Encode(param);
my.SendPkt(my.def.CBT_CMD_PSID, cmdPkt);
ret = my.WaitReplyPkt(my.def.CBT_CMD_PSID, my.def.IEEE1284_4_CMD_OPENCHANNEL);
data = my.m_CbtCodec.DecodeData(ret.packet.slice(my.def.CBT_HEADER_SIZE));
return {
ret: data.result,
priToSecPktSize: data.priToSecPktSize,
secToPriPktSize: data.secToPriPktSize,
maxOutstandingCredit: data.maxOutstandingCredit,
credit: data.credit
};
}
このようにして、startPrintJobでは、DOT4のInitコマンドを発行し、DOT4モードに移行したのち、OpenChannelコマンドでチャネルを確立する。
次にUSBポートモニター402は、JavaScriptのプログラム4011のwritePrintDataを呼び出す。writePrintDataでは、startPrintJobでオープンしたチャネルに対して、印刷ジョブのデータを送信していく。まずは、プロパティバッグ317からPSIDを取得し、startPrintJobで確立したチャンネルのデータを取得する。そして、CreditBasedTrasnferManager(CBTMgr)のSendを呼び出して、印刷ジョブの送信を行う。送信したら、プロパティバッグ317のPSIDを更新しておく。
function writePrintData(usbJobContext, printData, printerStream, writePrintDataProgress, printerBidiSchemaResponses) {
dot4Cache = json.parse(usbJobContext.jobPropertyBag.GetString("Dot4Cache"));
cbtMgr = CbtFac("2000", usbIo);
if (cbtMgr !== undefined) {
cbtMgr.SetProperty(dot4Cache);
ret = cbtMgr.Send(dot4Cache.openedPsid, printData);
newProperties = cbtMgr.GetProperty();
newProperties.openedPsid = dot4Cache.openedPsid;
usbJobContext.jobPropertyBag.SetString("Dot4Cache", json.stringify(newProperties));
}
return retVal;
}
Sendでは、WriteDataを使用してデータを送信する。
that.Send = function (psid, data) {
var ret = my.WriteData(psid, data, true);
return ret;
}
WriteDataでは、印刷ジョブデータの送信を行うため、タイムアウトを設定し、キャッシュデータに保存して、write用のクレジットの残り数を確認し、パケットを送信する。WritePrintDataは、印刷ジョブがすべて一度の呼び出しで渡されるわけではなく、いろいろなデータサイズで多数呼び出される。DOT4では、クレジットで指定されたバッファの最大値のデータを送付したほうが、通信速度としても有利なので、バッファの値までデータをキャッシュしてから、送信するようにしている。
my.WriteData = function (psid, data, isJobDataCaching) {
ret1 = my.m_CbtChannelList.GetChannelTimeout(psid);
writeTimeout = ret1.writeTimeout;
ret2 = my.m_CbtChannelList.GetNumCredit(psid, my.def.CBT_WRITE_DIRECTION);
curCredit = ret2.curCredit;
isJobChannel = my.m_CbtChannelList.IsJobChannel(psid);
isWriteJobCache = false;
while ((time.GetTime() < writeTimeout) && (remainDataSize > 0)) {
isWriteJobCache = false;
if (isJobDataCaching && isJobChannel) {
if (my.m_JobCacheDataSize === my.m_JobCacheSize) {
isWriteJobCache = true;
}
else if (my.m_JobCacheDataSize > 0) {
if (remainDataSize + my.m_JobCacheDataSize >= my.m_JobCacheSize) {
Array.prototype.push.apply(my.m_JobCacheBuff, data.slice(writtenDataSize, writtenDataSize + my.m_JobCacheSize - my.m_JobCacheDataSize));
writtenDataSize += (my.m_JobCacheSize - my.m_JobCacheDataSize);
remainDataSize -= (my.m_JobCacheSize - my.m_JobCacheDataSize);
my.m_JobCacheDataSize = my.m_JobCacheSize;
isWriteJobCache = true;
} else {
Array.prototype.push.apply(my.m_JobCacheBuff, data.slice(writtenDataSize, writtenDataSize + remainDataSize));
my.m_JobCacheDataSize += remainDataSize;
writtenDataSize += remainDataSize;
remainDataSize = 0;
}
} else {
if (remainDataSize >= my.m_JobCacheSize) {
} else {
Array.prototype.push.apply(my.m_JobCacheBuff, data.slice(writtenDataSize, writtenDataSize + remainDataSize));
my.m_JobCacheDataSize = remainDataSize;
writtenDataSize += remainDataSize;
remainDataSize = 0;
}
}
}
if ((remainDataSize <= 0) && !isWriteJobCache) {
break;
}
if (curCredit === 0) {
if (sleepTime === 0) {
sleepTime = my.CREDIT_ZERO_TIMEOUT_CONSTANT;
} else {
sleepTime *= 2;
if (sleepTime > my.CREDIT_ZERO_TIMEOUT_LIMITER) {
sleepTime = my.CREDIT_ZERO_TIMEOUT_LIMITER;
}
Sleep(sleepTime);
}
creditRequestTime = Time();
ret = my.CbtCreditRequest(psid, 0xFFFF, 0xFFFF);
ret3 = my.m_CbtChannelList.GetNumCredit(psid, my.def.CBT_WRITE_DIRECTION);
ret = ret3.ret;
curCredit = ret3.curCredit;
if (curCredit === 0) {
if (++zeroCreditCount > my.CREDIT_REQUEST_LIMIT) {
break;
} else {
continue;
}
} else {
sleepTime = 0;
writeTimeout += creditRequestTime.GetTime();
}
}
if (isWriteJobCache) {
ret = my.m_CbtPkt.SendDataPkt(psid, my.m_JobCacheBuff.slice(0, my.m_JobCacheDataSize));
} else {
ret = my.m_CbtPkt.SendDataPkt(psid, data.slice(writtenDataSize, writtenDataSize + remainDataSize));
}
if (ret > 0) {
--curCredit;
if (isWriteJobCache) {
if (ret === my.m_JobCacheDataSize) {
my.m_JobCacheBuff.length = 0;
my.m_JobCacheDataSize = 0;
} else {
my.m_JobCacheBuff.splice(0, ret);
my.m_JobCacheDataSize -= ret;
}
} else {
writtenDataSize += ret;
remainDataSize -= ret;
}
} else {
break;
}
}
if ((writtenDataSize > 0) || (ret >= 0)) {
ret = writtenDataSize;
}
return ret;
}
CbtCreditRequestでは、クレジットをSSIDに対して要求する。クレジットを受け取ったら、write用クレジットを更新する。
my.CbtCreditRequest = function (psid, creditRequest, maxOutstandingCredit) {
ret1 = my.m_CbtChannelList.GetSsid(psid);
ret2 = my.m_CbtPkt.CmdCreditRequest(psid, ret1.ssid, creditRequest, maxOutstandingCredit);
my.m_CbtChannelList.MngCredit(psid, my.def.CBT_WRITE_DIRECTION, ret2.credit);
return my.def.CBT_SUCCESS;
}
CmdCreditReqeustでは、クレジットリクエストのパケットを生成し、送信する。
that.CmdCreditRequest = function (psid, ssid, creditRequest, maxOutstandingCredit) {
param = {
header: {
psid: my.def.CBT_CMD_PSID,
ssid: my.def.CBT_CMD_SSID,
length: my.CBT_CMD_CREDITREQUEST_SIZE_STD,
credit: 1,
control: my.CBT_CONTROL_CMD
},
data: {
command: my.def.IEEE1284_4_CMD_CREDITREQUEST,
psid: psid,
ssid: ssid,
maxOutstandingCredit: maxOutstandingCredit
}
}
cmdPkt = my.m_CbtCodec.Encode(param);
my.SendPkt(my.def.CBT_CMD_PSID, cmdPkt);
ret = my.WaitReplyPkt(my.def.CBT_CMD_PSID, my.def.IEEE1284_4_CMD_CREDITREQUEST);
data = my.m_CbtCodec.DecodeData(ret.packet.slice(my.def.CBT_HEADER_SIZE));
return {
ret: data.result,
credit: data.credit
};
}
クレジットが正しく受け取れたら、印刷ジョブのデータを送信するために、SendDataPktで、パケットを作成して送信する。
that.SendDataPkt = function (psid, data) {
ret1 = my.CreateDataPkt(psid, data);
ret2 = my.SendPkt(psid, ret1.packet);
}
CreateDataPktでは、印刷ジョブのためのパケットを生成して、エンコードする。
my.CreateDataPkt = function (psid, data) {
ret1 = my.m_CbtChannelList.GetPktSizeFromChannel(psid);
ret2 = my.m_CbtChannelList.GetSsid(psid);
dataPktSize = Math.min(ret1.priToSecPktSize, data.length + my.def.CBT_HEADER_SIZE);
param = {
header: {
psid: psid,
ssid: ret2.ssid,
length: dataPktSize,
credit: piggy,
control: my.CBT_CONTROL_DATA_PKT
},
data: data.slice(0, dataPktSize - my.def.CBT_HEADER_SIZE)
}
packet = my.m_CbtCodec.Encode(param);
return { ret: dataPktSize - my.def.CBT_HEADER_SIZE, packet: packet };
}
以上のように、writePrintDataでは、プリントマネージャ318から渡される印刷データをキャッシュしながら、印刷ジョブデータの送信を行う。
最後に、USBポートモニター402は、JavaScriptのプログラム4011のendPrintJobを呼び出す。endPrintJobでは、プロパティバッグ317からPSIDを取得し、startPrintJobで確立したチャンネルのデータを取得する。そして、必要であればキャッシュをクリアし、cbtMgrのCloseを呼び出すことで、印刷ジョブデータの送信とDOT4を終了する。
function endPrintJob(usbJobContext, printerStream, printerBidiSchemaResponses) {
dot4Cache = json.parse(usbJobContext.jobPropertyBag.GetString("Dot4Cache"));
cbtMgr = CbtFac("2000", usbIo);
if (cbtMgr !== undefined) {
cbtMgr.SetProperty(dot4Cache);
if (!dot4Cache.isAborted) {
for (; ;) {
ret = cbtMgr.FlushJobCache(dot4Cache.openedPsid);
if (ret === 0) {
break;
} else if (ret > 0) {
} else {
break;
}
}
}
cbtMgr.Close(dot4Cache.openedPsid);
}
return retVal;
}
Closeでは、CloseChannelのコマンドを送信してチャンネルをクローズした後、Exitコマンドを送信してDOT4モードを終了する。
that.Close = function (psid) {
if (my.m_CbtChannelList.IsJobChannel(psid)) {
my.m_JobCacheBuff.length = 0;
my.m_JobCacheSize = 0;
my.m_JobCacheDataSize = 0;
}
var ret = my.CbtCloseChannel(psid);
if (ret === my.def.CBT_SUCCESS) {
if (!my.m_CbtChannelList.IsExistOpenedChannel()) {
if (my.ExitDot4() !== my.def.CBT_SUCCESS) {
}
my.m_CbtChannelList.DestroyAllChannel();
}
}
my.m_CbtChannelList.DestroyChannel(psid);
my.ExitDot4();
my.m_CbtChannelList.DestroyAllChannel();
return ret;
}
CbtCloseChannelではCmdCloseChannelを呼び出して、CloseChannelを行う。
my.CbtCloseChannel = function (psid) {
ret1 = my.m_CbtChannelList.GetSsid(psid);
ret2 = my.m_CbtPkt.CmdCloseChannel(psid, ret1.ssid);
}
CmdCloseChannelでは、CloseChannelのパケットを生成して、パケットを送信する。
that.CmdCloseChannel = function (psid, ssid) {
param = {
header: {
psid: my.def.CBT_CMD_PSID,
ssid: my.def.CBT_CMD_SSID,
length: my.CBT_CMD_CLOSECHANNEL_SIZE,
credit: 1,
control: my.CBT_CONTROL_CMD
},
data: {
command: my.def.IEEE1284_4_CMD_CLOSECHANNEL,
psid: psid,
ssid: ssid
}
}
cmdPkt = my.m_CbtCodec.Encode(param);
my.SendPkt(my.def.CBT_CMD_PSID, cmdPkt);
ret = my.WaitReplyPkt(my.def.CBT_CMD_PSID, my.def.IEEE1284_4_CMD_CLOSECHANNEL);
data = my.m_CbtCodec.DecodeData(ret.packet.slice(my.def.CBT_HEADER_SIZE));
return data.result;
}
ExitDot4では、CbtExitを。CbtExitでは、CmdExitをそれぞれ呼び出して、Exitコマンドを送信する。
my.ExitDot4 = function () {
var ret = my.CbtExit();
return ret;
}
my.CbtExit = function () {
var ret = my.m_CbtPkt.CmdExit();
return ret;
}
CmdExitでは、Exitコマンドのパケットを生成して、パケットを送信する。
that.CmdExit = function () {
param = {
header: {
psid: my.def.CBT_CMD_PSID,
ssid: my.def.CBT_CMD_SSID,
length: my.CBT_CMD_EXIT_SIZE,
credit: 1,
control: my.CBT_CONTROL_CMD
},
data: {
command: my.def.IEEE1284_4_CMD_EXIT
}
}
cmdPkt = my.m_CbtCodec.Encode(param);
my.SendPkt(my.def.CBT_CMD_PSID, cmdPkt);
ret = my.WaitReplyPkt(my.def.CBT_CMD_PSID, my.def.IEEE1284_4_CMD_EXIT);data = my.m_CbtCodec.DecodeData(ret.packet.slice(my.def.CBT_HEADER_SIZE));
return data.result;
}
以上のように、endPrintJob()では、CloseChannelとExitコマンドを使用して、DOT4の印刷を終了する。
次に、プリンター1042に対して、構成情報取得でオプションの装着状態やステータスを取得するシーケンスを説明する。
USBポートモニター402は、Bidiインターフェースが呼び出されると、JavaScriptのプログラム4011のgetSchemasを呼び出す。getSchemasでは、CBTMgrのOpenを管理チャネルでオープンし、Sendを呼び出した後、Recvを呼び出して情報を取得する。取得し終われば、Closeする。
function getSchemas(driverProperties, printerStream, schemaRequests, printerBidiSchemaResponses) {
cbtOpenRet = cbtMgr.Open(cbtDef.CBT_ADMIN_SSID);
// create data
m_CbtMgr.Send(m_Psid, data);
ret = m_CbtMgr.Recv(m_Psid, readSize);
cbtMgr.Close(adminPsid);
return result;
}
Sendは前述の印刷ジョブの部分に出てきたので、ここではRecvのシーケンスを説明する。RecvはReadDataを呼び出す。
that.Recv = function (psid, readSize) {
var ret = my.ReadData(psid, readSize);
return ret;
}
ReadDataは、チャネルのタイムアウトを指定した後、パケット数を取得して、データがなければ、RecvPktでパケットからデータを取得する。その後、GetDataFromChannelでチャネルからデータを読み込んでキャッシュに保存する。
my.ReadData = function (psid, readSize) {
ret1 = my.m_CbtChannelList.GetChannelTimeout(psid);
readTimeout = ret1.readTimeout;
while ((time.GetTime() < readTimeout) && (remainDataSize > 0)) {
ret2 = my.m_CbtChannelList.GetNumPktInChannel(psid);
if (ret2.numPkt > 0) {
ret3 = my.m_CbtChannelList.GetDataFromChannel(psid, remainDataSize);
if (ret3.ret >= 0) {
Array.prototype.push.apply(data, ret3.data);
readDataSize += ret3.data.length;
remainDataSize -= ret3.data.length;
} else {
return { ret: my.def.CBT_ERROR_FATAL, data: data };
}
} else if (ret2.numPkt === 0) {
ret4 = my.m_CbtPkt.RecvPkt(psid);
if ((ret4 === my.def.CBT_ERROR_EXIT_DOT4) || (ret4 === my.def.CBT_ERROR_FATAL)) {
return { ret: ret4, data: data };
}
} else {
return { ret: my.def.CBT_ERROR_FATAL, data: data };
}
}
return { ret: readDataSize, data: data };
}
return that;
}
GetDataFromChannelでは、読み込んだデータのデコードを行って、バッファを取得する。
that.GetDataFromChannel = function (dataSize) {
while (totalReadSize < dataSize) {
if (m_ReadNumPkt === 0) {
break;
}
if (m_ReadPktRemainSize === 0) {
header = m_CbtCodec.DecodeHeader(m_ReadBuffer.splice(0, def.CBT_HEADER_SIZE));
dataReadSize = 0;
if (header.length - def.CBT_HEADER_SIZE > dataSize - totalReadSize) {
dataReadSize = dataSize - totalReadSize;
Array.prototype.push.apply(data, m_ReadBuffer.splice(0, dataReadSize));
m_ReadPktRemainSize = header.length - def.CBT_HEADER_SIZE - dataReadSize;
} else {
dataReadSize = header.length - def.CBT_HEADER_SIZE;
Array.prototype.push.apply(data, m_ReadBuffer.splice(0, dataReadSize));
m_ReadNumPkt -= 1;
}
totalReadSize += dataReadSize;
}
} else {
dataReadSize = 0;
if (m_ReadPktRemainSize > dataSize - totalReadSize) {
dataReadSize = dataSize - totalReadSize;
Array.prototype.push.apply(data, m_ReadBuffer.splice(0, dataReadSize));
} else {
dataReadSize = m_ReadPktRemainSize;
Array.prototype.push.apply(data, m_ReadBuffer.splice(0, dataReadSize));
m_ReadNumPkt -= 1;
}
totalReadSize += dataReadSize;
m_ReadPktRemainSize -= dataReadSize;
}
}
return { ret: def.CBT_SUCCESS, data: data };
}
以上のようにJavaScriptのプログラム4011を作成することで、V4プリンタードライバー1052において、USBポートで、DOT4をサポートすることができる。DOT4をサポートすることで、印刷ジョブを送信しながら、構成情報を取得するなどの双方向で同時に通信することができる。
1042 プリンター、1052 プリンタードライバー、317 プロパティバッグ、
318 プリントマネージャ(スプーラ)、319 ポートモニター、
401 USBBidi Extender、4011 JavaScript、
402 USBポートモニター、403 USBPRINT
318 プリントマネージャ(スプーラ)、319 ポートモニター、
401 USBBidi Extender、4011 JavaScript、
402 USBポートモニター、403 USBPRINT
Claims (1)
- プロパティバッグにポートの情報とチャネルの情報とクレジットを保存する手段と、プロパティバッグからチャネルとクレジットを取得して、データを送受信したら更新する手段と、印刷ジョブを送信するときと構成情報を取得するときで重ならないように、プロパティバッグに別々の情報として保持しておく手段を持つことを特徴とするV4プリンタードライバーのUSBJavaScriptプログラム。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
JP2014144644A JP2016021157A (ja) | 2014-07-15 | 2014-07-15 | Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
JP2014144644A JP2016021157A (ja) | 2014-07-15 | 2014-07-15 | Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム |
Publications (1)
Publication Number | Publication Date |
---|---|
JP2016021157A true JP2016021157A (ja) | 2016-02-04 |
Family
ID=55265958
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
JP2014144644A Pending JP2016021157A (ja) | 2014-07-15 | 2014-07-15 | Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム |
Country Status (1)
Country | Link |
---|---|
JP (1) | JP2016021157A (ja) |
Cited By (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US10452326B2 (en) | 2017-07-06 | 2019-10-22 | Ricoh Company, Ltd. | Information processing apparatus, information processing method, and non-transitory computer-readable storage medium |
US10846026B2 (en) | 2018-03-09 | 2020-11-24 | Ricoh Company, Ltd. | Information processing apparatus, information processing system, and non-transitory recording medium storing instructions for executing an information processing method |
-
2014
- 2014-07-15 JP JP2014144644A patent/JP2016021157A/ja active Pending
Cited By (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US10452326B2 (en) | 2017-07-06 | 2019-10-22 | Ricoh Company, Ltd. | Information processing apparatus, information processing method, and non-transitory computer-readable storage medium |
US10846026B2 (en) | 2018-03-09 | 2020-11-24 | Ricoh Company, Ltd. | Information processing apparatus, information processing system, and non-transitory recording medium storing instructions for executing an information processing method |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
JP4543340B2 (ja) | 画像処理装置および画像形成システムとプログラム | |
JP6604834B2 (ja) | 情報処理装置及びその制御方法、プログラム | |
JP6331910B2 (ja) | 情報処理装置、情報処理システム、及びプログラム | |
US9274736B2 (en) | Information processing apparatus, output system, information processing method, and recording medium storing information processing program | |
JP2003029941A (ja) | 情報処理装置および印刷装置とその制御方法 | |
JP6000828B2 (ja) | 情報処理装置、印刷サーバーシステム、その制御方法、およびプログラム。 | |
JP6315965B2 (ja) | 情報処理装置、プログラム及び制御方法 | |
JP6289276B2 (ja) | 情報処理装置およびプログラム、制御方法 | |
CN107102826B (zh) | 图像处理设备及其控制方法 | |
US8804161B2 (en) | Information processing device, information processing system, control method, and storage medium | |
JP2016021157A (ja) | Usb接続の一台の機器で複数の機能を持つことを可能にするプログラム | |
US8861008B2 (en) | Bidirectional communication technique between an information processing apparatus and an image processing apparatus | |
US10747487B2 (en) | Printing extension computing device | |
JP2018081416A (ja) | 情報処理装置、情報処理装置の制御方法、及びプログラム | |
CN103324450A (zh) | 信息处理设备和存储介质 | |
JP6768544B2 (ja) | 情報処理装置、制御方法およびプログラム | |
US20190187937A1 (en) | Printing computing device for operating a multi-function printing device | |
JP3994984B2 (ja) | プリント支援モジュール、記録媒体及びプリントジョブ発行方法 | |
EP3070923B1 (en) | Printing system capable of printing in any one of plural image forming apparatuses over network, image forming apparatus, and printing method | |
JP5298725B2 (ja) | 画像処理装置、画像処理方法、画像処理システムおよびプログラム | |
US11068214B2 (en) | Printing computing device for printing PPL jobs having video data and methods for use with a printing system for printing PPL jobs having video data | |
JP2015204045A (ja) | 情報処理装置及びその制御方法 | |
US10817233B2 (en) | Printing computing device for operating a multi-function printing device | |
JP2013161196A (ja) | ウェブ(Web)ページ印刷システム、方法、及び、プログラム | |
JP4641392B2 (ja) | 制御装置、通信処理方法およびプログラム |