Top > MyPage
 

Linuxサーバとシリアルポート通信ができるようになるまでver.1.1

Linuxの方の準備

柴田さんの書かれたドキュメントの方が詳しいので、先にこちらをご覧ください。

Fedora 9のインストール

ここからi386のインストールDVDをダウンロード→DVDに焼いてインストール。

(今回は雑誌についているDVDでインストールしている)

JDKのインストール

ここから、jdk-6u10-rc-bin-b28-linux-i586-21_jul_2008.binをダウンロード。

/usr/java/にbinファイルをを置いて実行。/usr/java/jdk1.6.0_10/にインストールする。

BlazeDS turnkeyのインストール

adobeのサイトから、blazeds_turnkey_3-0-0-544.zipをダウンロード。

中にはTomcatのVersion 6.0.14が既に入っているので、それをそのまま使う。

/usr/java/blazedsという名前で配置。

ディレクトリ構造は、以下のようになっている。

[<figure src="" alt=""></figure> is illegal in <p>]

環境変数の設定

/etc/profileに、次のように付け足す。

PATH=/usr/java/JDK/bin:/usr/java/blazeds/tomcat/bin:$PATH

export PATH

JAVA_HOME=/usr/java/JDK

export JAVA_HOME

TURNKEY_HOME=/usr/java/TURNKEY

export TURNKEY_HOME

いつもと違うのは、CATALINA_HOMEを設定していない所。

TURNKEY_HOMEがその役割を果たしている。

webアプリケーションの配置

SamPIJavaでテスト

/usr/java/blazeds/tomcat/webapps/ にwebアプリケーションを置く。

今回はSamPIJavaでテスト。

XalanとXerces

柴田さんのドキュメントを参考にして、

をダウンロード。

展開して、jarファイルをusr/java/blazeds/tomcat/lib/に置く。

両方に共通するファイルがあり、先にXalanの方を置き、次にXercesのjarファイルを置く。(結果的に共通するファイルは上書き)

RXTXのインストール

RXTXのサイトからrxtx-2.1-7-bins-r2.zipをダウンロード

展開して、RXTXcomm.jar/SamPIJava/WEB-INF/libに置く。

ドライバファイルもあるので、

  • Linux/i686-unknown-linux-gnu/librxtxParallel.so
  • Linux/i686-unknown-linux-gnu/librxtxSerial.so

の2つをJAVA_HOME/jre/lib/i386に置く。これでRXTXが使えるようになる。

注意事項

Springのバージョンが2.0.7だと、

[<source></source> is illegal in <p>]


というエラーになり、(コンテキストの初期化に失敗したりする)404でサイトに繋がらない。
tomcat6系とjdkの1.6系にspringが対応していないからだと思われる。
          Spring Framework
のに差し替えると、問題なく動いた。

          ※BlazeDSの動作環境がようなので、Tomcat自体も6.0を使った方が良いと思われる。


実際にTomcat5.5上ではエラーが出て動かないのに、6.0では何事もなくスムーズに動いたという経験もある。
(他にも原因があるのかもしれないが)
        

spring-framework-2.5.5/dist/modulesの中にある14個のjarファイルをWEB-INF/libに入れ、ver2.0.7の方のspring-*-*.jarファイルを消去している。

シリアルポートから送ったデータをFlexで表示させる

はじめに

通信はこんな感じで行います。

データの流れ

RS232C通信の準備

台貫からはテラタームを使ってデータを送信。送信されたデータをLinux側で受け取るために、

SerialPortReceive.javaというファイルを準備。

参考にしたサイトはこちら

SerialPortReceive.java

package com.chikkun.test;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.TooManyListenersException;

public class SerialPortReceive {

    CommPortIdentifier portId;

    SerialPort port;

    String rtn;

    public SerialPortReceive() {
        super();
        this.openSerialPort();
        this.setSerialPort();
   }

    // ポートオープン
    boolean openSerialPort() {
        try {
            portId = CommPortIdentifier.getPortIdentifier("/dev/ttyS0");
            port = (SerialPort) portId.open("RS232C", 2000);
        } catch (NoSuchPortException e) {
            e.printStackTrace();
            return false;
        } catch (PortInUseException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    // シリアルポートの設定
    boolean setSerialPort() {
        try {
            port.setSerialPortParams(9600, // 通信速度[bps]
                    SerialPort.DATABITS_8, // データビット数
                    SerialPort.STOPBITS_1, // ストップビット
                    SerialPort.PARITY_NONE // パリティ
                    );
            port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
        } catch (UnsupportedCommOperationException e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    public void waitCharacters() throws IOException {

    this.enableListener();
}

    class SerialPortListener implements SerialPortEventListener {
        public void serialEvent(SerialPortEvent event) {
            if (event.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                read();
            }
        }
    }

    void enableListener() {
        try {
            port.addEventListener(new SerialPortListener());
            port.notifyOnDataAvailable(true);
        } catch (TooManyListenersException e) {
            // エラー処理
        }
    }

    //データの受信(ポーリング)
    public  String read() {
        int count = 0;

        StringBuffer received = new StringBuffer();
        try {

            InputStream in = port.getInputStream();

            while (true) {
              byte[] buffer = new byte[1];              
                int numRead = in.read(buffer);
                if (numRead == -1) {
                    // ストリーム終わり
                    System.err.println("EOF!!  ");
                    break;
                } else if (numRead == 0) {
                    // データなし
                    if (count > 5) {
                        // 1秒データなしが続いたらbreak
                        System.err.println("Timeout!!  ");
                        break;
                    } else {
                        count++;
                    }
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        // 割り込まれても何もしない
                    }
                } else {
                    count = 0;
                    // bufferから読み出し処理
                    String str = new String(buffer);


                    //頭と後ろの半角全角スペースを削除
                    str.replaceAll("^[  ]*", "").replaceAll("[  ]*$", "");

                    received.append(str);

                    if (str.matches(".*\\n.*") || str.matches(".*\\r.*")) {
                         // 改行が来たらbreak
                         System.err.println("Break!!  ");
                         break;

                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("Exception!!  ");
        }

        //receivedをString型に変更
        String received_str = received.toString();

        //頭と後ろの半角全角スペースを削除
received_str.replaceAll("^[  ]*", "").replaceAll("[  ]*$", "");

        String sended = received_str;

        //stopという文字列があればポートを閉じる
        if (sended.matches("^.*stop.*$")) {
            System.err.println("port close");
            sended = "Port close";
            port.removeEventListener();
            port.close();
        }
        port.removeEventListener();
        port.close();
        return sended;

    }
}

上記のサイトを参考にして作っていたが、文字を送信することはできるが

文字列の先頭の部分しかFlexに表示されないという状態になってしまった。(Helloと送信しても表示はHのみ)

テラタームは何も入力されていない状態のときはしているため、

Helloという文字列を送信しているつもりでも、実際は

H          e       l(以下略)

という形でデータが送られている。

そのため、という作業が必要になる。

Linux→Windows(Flex)の準備

LinuxからWindows(Flex)へデータを送る方法として、今回はBlazeDSのメッセージング機能を使っている。

BlazeDSのSamples6,Publish/Subscribe Messaging (Data Push Use Case)を参考にしている。

Feed.java

package com.chikkun.test;

import flex.messaging.MessageBroker;
import flex.messaging.messages.AsyncMessage;
import flex.messaging.util.UUIDUtils;

public class Feed {
    private static FeedThread thread;

    public Feed() {
    }

    public void start() {
        if (thread == null) {
            thread = new FeedThread();
            thread.start();

        }
    }

    public void stop() {
        thread.running = false;
        thread = null;

    }

    public static class FeedThread extends Thread {

        public boolean running = true;

        public void run() {

            //メッセージブローカーのインスタンスを取得
            MessageBroker msgBroker = MessageBroker.getMessageBroker(null);

            String test_str = "";
            SerialPortReceive spt = new SerialPortReceive();
            spt.openSerialPort();
            spt.setSerialPort();

            //UUID (Universally Unique Identifier) を作成
            String clientID = UUIDUtils.createUUID();

            while (running) {
                spt.openSerialPort();
                spt.setSerialPort();
                test_str=spt.read();

                //非同期メッセージのインスタンスを作成                
                AsyncMessage msg = new AsyncMessage();

                //宛先を設定
                msg.setDestination("feed");

                //クライアントIDにユニークなIDを設定
                msg.setClientId(clientID);

                //メッセージIDにユニークなIDを設定
                msg.setMessageId(UUIDUtils.createUUID());

                //タイムスタンプを設定
                msg.setTimestamp(System.currentTimeMillis());

                //Bodyにシリアルポートから読み込んだデータをセット
                msg.setBody(test_str);

                //メッセージブローカーを使って作成したメッセージのプッシュ配信を行う
                msgBroker.routeMessageToService(msg, null);

                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                }

            }
        }
    }

}

テラタームから取得したデータをサーバからFlexへpushさせる。

メッセージング機能を使うために、WEB-INF/flexディレクトリにあるmessaging-config.xmlにdestinationを記述する。

messaging-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" \
    class="flex.messaging.services.MessageService">

    <adapters>
<adapter-definition id="actionscript" \
    class="flex.messaging.services.messaging.adapters.ActionScriptAdapter" \
    default="true" />
<!-- <adapter-definition id="jms" \
    class="flex.messaging.services.messaging.adapters.JMSAdapter"/> -->
    </adapters>

    <default-channels>
		<channel ref="my-streaming-amf"/>
		<channel ref="my-polling-amf"/>
    </default-channels>

    <destination id="feed">
    	<properties>
    		<factory>spring</factory>
    		<source>feed</source>
    	</properties>

        <channels>           
			<channel ref="my-streaming-amf"/>
        </channels>        

    </destination>

</service>

sample6では、サーバーからのデータpushをjspでstart,stopしていたので、同じようにしてやってみた。

startfeed.jsp

<%@page import="com.chikkun.test.Feed"%>
<%
	try {
		Feed feed = new Feed();
		feed.start();
		out.println("Feed Started");
	} catch (Exception e) {
		out.println("A problem occured while starting the feed: \
    "+e.getMessage());
	}
%>

stopfeed.jsp

<%@page import="com.chikkun.test.Feed"%>
<%
	try {
		Feed feed = new Feed();
		feed.stop();
		out.println("Feed Stopped");
	} catch (Exception e) {
		out.println("A problem occured while stopping the feed: \
    "+e.getMessage());
	}
%>

クライアント(Flex)側の準備

main.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" \
    backgroundColor="#FFFFFF">
	<mx:Script>
		

			import mx.messaging.messages.IMessage;

			private function messageHandler(message:IMessage):void
			{
				pushedValue.text = ""+ message.body;	
			}

		

	</mx:Script>

	<mx:Consumer id="labelconsumer" destination="feed" \
    message="messageHandler(event.message)"/>

	<mx:Button label="Subscribe to 'feed' destination" \
    click="consumer.subscribe()" enabled="{!consumer.subscribed}"/>
	<mx:Button label="Unsubscribe from 'feed' destination" \
    click="consumer.unsubscribe()" enabled="{consumer.subscribed}"/>

	<mx:TextInput id="labelpushedValue"/>

</mx:Application>

          
(※mx:Script,/mx:Scriptの部分にコメントアウトが必要だが、上手く表示されなかったので後で調べて直します。)


クライアント側での受信用コンポーネントが          Consumerconsumer.subscribe()メソッド
を呼ぶことで受信を開始する。受信すると送出されるイベントが          messageイベント

まとめ


ここまでくるのに非常に時間がかかったが、テラタームでHelloと送れば
FlexでHelloとなんとか表示してくれるようになった。

この例ではjspでデータプッシュをスタートさせて、flexでそれを受信開始してと面倒なので、
ボタン一つ押すだけで、すべて始まるようにする必要があると考えた。

しかしそのためにはWEB-INF/Flexの二つの設定ファイル(今回で言えばmessaging-config.xmlとremoting-config.xml)に
同じdestination idを書く必要があると思うのだが、このような使い方は可能なのだろうか。

BlazeDSのメッセージング機能(channel等)の情報が詳しく載っているサイトがあったのでメモ。
          BlazeDS messaging framework -kozy.heteml.jp