2

こんにちは、COM1 を介してカスタム デバイスと通信する Visual Studio 2008 C++ プロジェクトがあります。テストでは、(cmd.exe から使用した場合) 期待どおりに動作し、(args を介して送信された) データを送信し、ポートから読み取った次の 300 バイトを出力 (cout) することが示されています。

例:

cppprogram.exe -send RANDOM_HEXSTRING_HERE -C COM1 [0000FFABCD1873295287210173983198396918273 (...) 1278612851FFEB]

応答の形式は常に [hexstring] です。エラーの場合は -1 を返し、それ以外の場合は 0 を返します。cmd.exe で実行すると、外部デバイスの速度に応じて、「ゆっくり」、約 100 バイト、1 秒待機、さらに 100 バイトなどを出力することがわかります。データを生成します。ただし、デバイスは常に印刷中であり、プログラムは 300 バイトしか待機しないため、常に終了します。

この「システム」の上に構築するために、Java でプログラムを作成しています。クエリを次々とデバイスに送信し、出力を処理することになっています。私は cpp プログラムを仲介として使用しています。

私が関数を持っているクエリの種類ごとに、多くの種類のクエリがありますが、各関数のコードは似ています。16 進文字列クエリと最終出力で検索する文字列のみが変更され、デバイスへのクエリは実行されません。並行して、プログラムは順次実行されるはずです。(現在の)コードは次のとおりです。

Main.java

main()
{
    (...)
    while( keepgoing )
    {
        (...)
        print( start Query A )
        QueryDeviceA()
        print( end Query A )
        (...)
    }
    (...)
}

public String QueryDeviceA()
{
    try
    {
        Runtime rt = Runtime.getRuntime();
        Process pr = rt.exec( "cppprogram.exe -C COM1 -rq" ); //rq performs the query A without having to put the hexstring    

        // any error message?
//      StreamGobbleAndReturnAsString errorGobbler = new StreamGobbleAndReturnAsString( pr.getErrorStream(), "ERR" );

        // any output?
        StreamGobbleAndReturnAsString outputGobbler = new StreamGobbleAndReturnAsString( pr.getInputStream(), "OUT" );

        // kick them off
System.out.println( "1" );
        System.out.flush();
        outputGobbler.start();
//      errorGobbler.start();

        int exitVal = pr.waitFor();

        outputGobbler.join();
//      errorGobbler.join();
        System.out.flush();
System.out.println( "2" );

        String returnValue = outputGobbler.getReturnValue();
        rt.gc();
System.out.println( "3" );

        if( returnValue == null ) return null;

        int start = returnValue.indexOf("[");
        int end = returnValue.indexOf("]", start);

        boolean datafound = false;
        if( start > -1 && end > -1 )
        {
            String returnpart = returnValue.substring(start+1, end);
            if( returnpart == null ) return null;

            start = 0;
            while( start < returnpart.length() )
            {
                do { start = returnpart.indexOf("0103", start); } while( start % 2 > 0 && start != -1 );
                if( start == -1 ) break;
                start+=2;

                do { end = returnpart.indexOf("04", start); } while( end % 2 > 0 && end != -1 );
                if( end == -1 ) break;
System.out.println( "4" );
                returnValue = TranslateEscapedData(returnpart.substring(start, end)); //doesnt have infinite loops
System.out.println( "5" );
                if( returnValue.length() != 30 ) continue;
                if( !"040F20".equals( returnValue.substring(12,18) ) ) continue;
System.out.println( "6" );
                //Verify Checksum
                if( !ValidChecksum(returnValue) ) continue; //doesnt have infinite loops
System.out.println( "7" );
                returnpart = returnValue.substring(18,26);
                datafound = true;
                break;
            }

            if( !datafound ) return null;
            return returnpart;
        }

        return null;
    }
    catch( Exception e )
    {
        System.out.println(e.toString());
        e.printStackTrace();
    }
    return null;
}

StreamGobbler.java

public class StreamGobbler extends Thread
{
    public InputStream is;
    public String type;
    public OutputStream os;

    private StreamGobbler()
    {
    }

    public StreamGobbler(InputStream is, String type)
    {
        this(is, type, null);
    }

    public StreamGobbler(InputStream is, String type, OutputStream redirect)
    {
        this.is = is;
        this.type = type;
        this.os = redirect;
    }

    @Override
    public void run()
    {
        try
        {
            PrintWriter pw = null;
            if( os != null ) pw = new PrintWriter(os);

            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while( (line = br.readLine()) != null )
            {
                if( pw != null ) pw.println(line);
                //System.out.println( type + ">" + line );
            }
            if( pw != null ) pw.flush();
        }
        catch( IOException ioe )
        {
            ioe.printStackTrace();
        }
    }
}

StreamGobbleAndReturnAsString.java

public class StreamGobbleAndReturnAsString extends util.StreamGobbler
{
    private String returnValue;

    public StreamGobbleAndReturnAsString(InputStream is, String type)
    {
        this(is, type, null);
    }

    public StreamGobbleAndReturnAsString(InputStream is, String type, OutputStream redirect)
    {
        super( is, type, redirect );
    }

    @Override
    public void run()
    {
        try
        {
            PrintWriter pw = null;
            if( os != null ) pw = new PrintWriter(os);

            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line = null;
            while( (line = br.readLine()) != null )
            {
                if( pw != null ) pw.println(line);
                //System.out.println( type + ">" + line );
                returnValue += line;
            }
            if( pw != null ) pw.flush();
            //br.close();
        }
        catch( IOException ioe )
        {
            ioe.printStackTrace();
        }
    }

    /**
     * @return the returnValue
     */
    public String getReturnValue() {
        return returnValue;
    }

    /**
     * @param returnValue the returnValue to set
     */
    public void setReturnValue(String returnValue) {
        this.returnValue = returnValue;
    }
}

ご覧のとおり、印刷デバッグを使用しています。これは、(外部デバイスを備えた) 運用 PC にデバッグ ツールがなく、遠くにあるためです。*.bat ファイルを使用してこの Java プログラムを実行し、それを開いている cmd.exe ウィンドウにドラッグすると、System.out.println 経由で送信された情報の表示が開始され、他のいくつかのクエリのデバッグ行が表示されます (約 5 行、外部プログラムが 300 バイトの出力を完了するのに 1 秒ほどかかるため、クエリ A に到達するまで一時停止し、現在は「3」で永久に停止します。コードを見てみると、3 で止まる理由がわかりません。

Gobbler はコードをコピーして貼り付けたもので、通常の while( ( line = br.readline() ) != null ) です。

私は、waitFor の前に結合を配置し、それらに 10000 ミリ秒のタイムアウトを追加し、そのうちの 1 つだけに、エラー ストリームをコメント アウトしました。 gobbler while ループの system.out.println(line) 。そして、永久にハングする前に出力されたデバッグ行または私にとってより論理的な他のエラーのみを変更します。例えば。スレッド結合をコメントアウトすると、通常は完全な出力が得られますが、完全ではない場合もあります。これは、gobbler の結果が機能する前に読み取っているためと考えられ、それが結合を追加している理由です。

ゴブラーのループ内で system.out.println を使用すると、ハングが以前のクエリに表示されるように見えるので、私の唯一のリードがありますが、これは明らかに私の現在の知識から外れているため、ここで質問します:)両方の gobblers の run メソッドの開始と終了、そしてどちらも開始と終了がうまくいっているようです。

コードは現在、Java 1.6.0_23-b05、Windows XP SP 2 で実行されています。実稼働マシンの構成は事実上ランダムであり、私の手の届かないところにありますが、それらに関連する場合は、そのメモを追加して、肩をすくめると思います。

編集:何か便利なものを持っているためだけに機能するストリームリーダー...

public String Query()
{
    try
    {
        Runtime rt = Runtime.getRuntime();
        Process pr = rt.exec( "cppprogram.exe -C COM1 -rq" );

        // any error message?
        StreamGobbleAndReturnAsString errorGobbler = new StreamGobbleAndReturnAsString( pr.getErrorStream(), "ERR" );

        // any output?
        StreamGobbleAndReturnAsString outputGobbler = new StreamGobbleAndReturnAsString( pr.getInputStream(), "OUT" );

        // kick them off
        outputGobbler.start();
        errorGobbler.start();

        int exitVal = pr.waitFor();

        outputGobbler.join();
        errorGobbler.join();

        String returnValue = outputGobbler.getReturnValue();
        rt.gc();

        if( exitVal == 0 ) //success
        {}
        else //cppprogram.exe returned error, do we care?
        {}

        if( returnValue == null ) return null;

        //process returnValue further if needed and then
        return returnValue;
    }
    catch( Exception e )
    {
        System.out.println(e.toString());
        e.printStackTrace();
    }
    return null;
}
4

1 に答える 1