プログラミング: 2019年8月アーカイブ

エラーが出てうまく動かなかった FeliCa カード読み込みマクロだが、結局、

com.sun.star.uno.RuntimeException: Couldn't convert SONY RC-S380/P on usb:003:008 to a UNO type; caught exception: <class 'AttributeError'>: 'ContactlessFrontend' object has no attribute 'getTypes', traceback follows
no traceback available...

というエラーのとおり、文字列しかセットできないセルに対して、ContactlessFrontend オブジェクトをそのまま突っ込んだのが悪かった。str(clf) のように、str 関数を使って文字列変換してからセルにセットすればよかったのだ。

というわけで、

# -*- coding: utf-8 -*-
# LibreOffice 用マクロ

import uno

def GetCardData( ):

    import nfc
    from nfc.clf import RemoteTarget

    doc = XSCRIPTCONTEXT.getDocument()
    sheet = doc.getSheets().getByName('sheet1')

    clf = nfc.ContactlessFrontend('usb')

    cell = sheet.getCellRangeByName('A1')
    cell.String = str(clf)

    tag = clf.connect(rdwr={
        'on-connect': lambda tag: False
    })

    cell = sheet.getCellRangeByName('A2')
    cell.String = str(tag)

というマクロを作成し、GetCardData を実行。

20190825_nfc1.jpg

すぐに、'A1'セルにカードリーダー情報がセットされる。

この状態で、カードリーダーで FeliCa カードを読むと、今度は 'A2'セルに FeliCa カード情報がセットされる。

20190825_nfc2.jpg

ばっちりやん、ばっちりやん。

これが一番操作的にはシンプルでええね。普通に Calc のファイルもダブルクリックで起動できるしね。(外部プログラムから FeliCa 情報を得ようとすると、Cals 自体を色々オプションをつけてコマンドラインから起動しないといけない)
外部プログラムで読んだ FeliCa カードの情報を LibreOffice の Calc に渡してみた。

もし、マクロで FeliCa カードが読めないのなら、この方法でも良いかな・・・と。
(ま、別のエントリーで書く予定だけど、マクロとして実行した Python プログラムで FeliCa カード読めたんだけどね)

1.LibreOffice Calc(表計算)を外部プログラムとの通信可能な状態で起動。

外部プログラムと通信する port 番号を指定して Calc を起動するので、メニュー等からではなく、コマンドで起動すること(Windows ならコマンドプロンプトからとか)

C:\Users\shinoda\Source\Python>"C:\Program Files\LibreOffice\program\soffice.exe" --calc --norestore --accept=socket,host=localhost,port=8100;urp

これで、外部プログラムとの通信用 port(8100番)を開けた Calc が起動する。

2.外部プログラムの実行。

今回起動する Python スクリプトはこんな感じ。
カードリーダー SONY RC-S380 で読みとった FeliCa カード情報を Calc の任意のセルに貼り付けるだけのプログラム。

import nfc
from nfc.clf import RemoteTarget

import uno
localContext = uno.getComponentContext()
resolver = localContext.ServiceManager.createInstanceWithContext(
    "com.sun.star.bridge.UnoUrlResolver", localContext)

# Calc に port=8100 で接続
ctx = resolver.resolve( "uno:socket,host=localhost,port=8100;urp;StarOffice.ComponentContext" )
smgr = ctx.ServiceManager

# ドキュメントオブジェクトの取得
desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
doc = desktop.getCurrentComponent()
sheet = doc.getSheets().getByName('Sheet1')

# カード読み込み待ち
clf = nfc.ContactlessFrontend('usb')

# 'A1' セルにカードリーダー情報をセット
cell = sheet.getCellRangeByName('A1')
cell.String = str(clf)

# カードがタッチされた(情報読み込み)
tag = clf.connect(rdwr={
    'on-connect': lambda tag: False
})

# 'A2' セルにカード情報をセット
cell = sheet.getCellRangeByName('A2')
cell.String = str(tag)

これを、コマンドプロンプトから実行(LibreOffice に付いてきたマクロ実行用の Python で実行)する。

C:\Users\shinoda\Source\Python>"C:\Program Files\LibreOffice\program\python.exe" .\cellset_to_calc2.py

3.起動と同時に、Calc の 'A1'セルにカードリーダー情報がセットされる

4.カードを読むと、その情報が 'A2'セルにセットされる

20190825_libreOffice1.jpg

ばっちりやね。

デメリットは、コマンドプロンプトから Calc を起動しないといけないことか。

でも、まあ、最悪この方法で LibraOffice にカード情報を渡せることはわかった。人類にとっては大きな一歩だ(笑)
LibreOffice のマクロ用 Python 環境に nfcpy モジュールをインストールした話は別エントリーで書いたが、結局、マクロとしてはうまく動かすことができなかった。

そこで、nfcpy モジュールのインストール自体は上手く行っているのか確認してみることに。

確認と言っても、テストで使用したマクロ用 Python プログラムの参考にした、


というサイト内のサンプルスクリプトを、LibreOffice のマクロ用 Python で実行してみただけである。

C:\Users\shinoda\Source\Python ディレクトリ以下に

import nfc
from nfc.clf import RemoteTarget

clf = nfc.ContactlessFrontend('usb')
print(clf)

tag = clf.connect(rdwr={
    'on-connect': lambda tag: False
})

print(tag)

こういう簡単なスクリプトを用意して(nfc_test3.py)、LibreOffice のマクロ用 Python(C:\Program Files\LibreOffice\program\python-core-3.5.7\bin\python.exe)を使って実行。

C:\Users\shinoda\Source\Python>"C:\Program Files\LibreOffice\program\python.exe" .\nfc_test3.py
SONY RC-S380/P on usb:003:008
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C3AFA2 PMM=00F1000000014300 SYS=88B4

ちゃんと、FeliCa カード読めるじゃん!

nfcpy モジュール自体のインストールは上手くいってるってことだから、やっぱマクロ環境から USB 機器を制御したりってことが、そもそも無理ってことなんかな???
この間作った「FeliCa カード読み込み」プログラムを LibreOffice のマクロにしてみようと思ったんだけど(OpenOffice.org や、その流れをくむ LibreOffice は、マクロ言語として Python や JavaScript が使える)、とりあえず実行してみると、

Python のスクリプト vnd.sun.star.script:nfc_macro_test.py$GetCardData?language=Python&location=share の実行中に Scripting Framework エラーが発生しました。

メッセージ: <class 'ImportError'>: No module named 'nfc'
<以下略>

とエラーになる。カードリーダーを制御するためのモジュールが読み込めないようだ。

LibreOffice はマクロ実行に、Windows にインストールされている Python ではなく、自前で用意した C:\Program Files\LibreOffice\program\python-core-3.5.7\bin\python.exe を使用する。そのため、モジュール類もこの Python 用にインストールする必要がある。

Python環境としてのLibreOfficeをより便利に使うために (2018-03-23)」というサイトを参考にインストールを行った。

1.まず、パッケージ管理ソフト pip のインストーラ(get-pip.py)をダウンロード

https://pip.pypa.io/en/stable/installing/ では、UNIX 系 OS 用に curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py で手に入れろと書いてあるが、Windows に curl コマンドはないので、直接ブラウザに https://bootstrap.pypa.io/get-pip.py を打って入手。
あとでコマンドを実行するディレクトリに置く。

2.get-pip.py を実行し、pip をインストールする。

俺は、get-pip.py を C:\Users\shinoda\Source\Python の下に置き、そのディレクトリに cd してからコマンド実行。

C:\Users\shinoda\Source\Python>set PATH=C:\Program Files\LibreOffice\program;%PATH%
C:\Users\shinoda\Source\Python>python get-pip.py --user

これで pip がインストールされる。

C:\Users\shinoda\AppData\Roaming\Python\Python35\Scripts ディレクトリ以下を見れば、pip.exe、pip3.5.exe、pip3.exe がインストールされている(全部同じもの)。
これで、外部モジュールをインストールする準備はできた。

3.pip で nfcpy をインストールする

C:\Users\shinoda\AppData\Roaming\Python\Python35\Scripts ディレクトリ以下にインストールされた pip を使うために path を通してから実行。

C:\Users\shinoda\Source\Python>set PATH=C:\Users\shinoda\AppData\Roaming\Python\Python35\Scripts;%PATH%
C:\Users\shinoda\Source\Python>pip install --user nfcpy

これでインストールされる。

4.パッケージ(fcpy や libusb1)が正常に入ったか確認

C:\Users\shinoda\Source\Python>pip list
Package    Version
---------- -------
libusb1    1.7.1
ndeflib    0.3.3
nfcpy      1.0.3
pip        19.2.2
pyDes      2.0.1
pyserial   3.4
setuptools 41.2.0
wheel      0.33.6

バッチリですな。

一旦 LibreOffice を閉じて、もう一度開き直してマクロ実行。これでも、No module named 'nfc' なエラーは出なくなる。

ま、新たに、

com.sun.star.uno.RuntimeException: Couldn't convert SONY RC-S380/P on usb:003:008 to a UNO type; caught exception: <class 'AttributeError'>: 'ContactlessFrontend' object has no attribute 'getTypes', traceback follows
no traceback available
 (Error during invoking function GetCardData in module file:///C:/Program%20Files/LibreOffice/share/Scripts/python/nfc_macro_test.py (<class 'uno.com.sun.star.uno.RuntimeException'>: Couldn't convert SONY RC-S380/P on usb:003:008 to a UNO type; caught exception: <class 'AttributeError'>: 'ContactlessFrontend' object has no attribute 'getTypes', traceback follows
no traceback available

  File "C:\Program Files\LibreOffice\program\pythonscript.py", line 907, in invoke
    ret = self.func( *args )
  File "C:\Program Files\LibreOffice\share\Scripts\python\nfc_macro_test.py", line 17, in GetCardData
    cell.String = clf
))

なんてエラーが出ちゃうんだけど(^^;

マクロで外部のデバイス(SONY RC-S380 カードリーダ)を制御出来んのかな?
ちょっと別の方法を考えるか・・・

Python のプログラム側から LibreOffice API を叩く方が良さげ。

<追記>
結局、エラーメッセージのとおり、ContactlessFrontend オブジェクトをそのままセルにぶち込もうとしたためのエラーだった。別エントリーで書いているように、String型に変換してセルに入れればまったく問題なかった(^^;
FeliCa カード読み込みの Python スクリプトを LibreOffice Calc にマクロで組み込みたいんだけど、tagtool.py をもとにスクリプトを書くには、tagtool.py が大きすぎて骨が折れそう。
なにせ、Python を触ったことはあるけど、ちゃんとプログラミングしたことはないからな(^^; 真似するにしても、元ソースの構文調べながらになるから tagtool.py は大きすぎる。

とか考えてたら、@NoTASKさんの「nfcpy 0.13.4でNFCのIDを読む」というページにシンプルなソース例が。

とりあえず print 分だけ修正し(ちゃんと、カッコで出力する文字列(変数も)を囲まないと、Python3 ではエラーになるようだ)、実行してみた。

2枚のカードを読んでみたが、それぞれ

pi@raspberrypi:~/test/pasori $ sudo ./nfc_test2.py
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C6A091 PMM=00F1000000014300 SYS=88B4
Type3Tag
b'012E4CD257C6A091'
pi@raspberrypi:~/test/pasori $ sudo ./nfc_test2.py
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C35873 PMM=00F1000000014300 SYS=88B4
Type3Tag
b'012E4CD257C35873'

このようにちゃんと ID も取れている。ばっちりじゃない。

この機能をマクロに組み込みたいのよね。

一発だけ読むのではなく、ずーっと読み込み続けるように無限ループさせて。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import nfc
import binascii
import time

def on_connect(tag):
    print(tag)
    print(tag.type)
    print(binascii.hexlify(tag.identifier).upper())

def main():
    while True:
        with nfc.ContactlessFrontend('usb') as clf:
            clf.connect(rdwr={'on-connect': on_connect})

        time.sleep(3)

main()

こんな感じで。

一度の実行で連続で読み込める。(time.sleep しないと、カード当てたとたんにザーっと連続で読み込むんで注意ね(笑)

pi@raspberrypi:~/test/pasori $ sudo ./nfc_test2.py
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C6A091 PMM=00F1000000014300 SYS=88B4
Type3Tag
b'012E4CD257C6A091'
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C35873 PMM=00F1000000014300 SYS=88B4
Type3Tag
b'012E4CD257C35873'
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C3AFA2 PMM=00F1000000014300 SYS=88B4
Type3Tag
b'012E4CD257C3AFA2'

これを元に、読み込んだ ID 情報をシートに貼り付けていくマクロを作ってみよう。

ところで、Python の無限ループって、Ctrl+C とかも受け付けなくなるんやね。Perl だと、Ctrl+C とかで止められるんだけどな。Python の場合、一定時間ごとに break する処理を書かないと、kill して止めるしかなくなる(^^;

マクロに組み込んだときにどうなるんかな?
Raspberry Pi 3 と Sony の RC-S380 というカードリーダーの組み合わせで FeliCa カードを読みたいのだが、けっこう同じことをしている人は多くてネット上に情報はたくさんある。

そこで、「RaspberryPiで!SONYのPaSoRi(RC-S380)で(NFC)Felica情報を読み取る!(KOKENSHAの技術ブログ)」という記事など参考に一応セットアップしてみたのだが、最後にサンプルプログラムを実行すると、

pi@raspberrypi:~/test/pasori/nfcpy/examples $ sudo python tagtool.py
This script requires Python 3

と、「Python 3 のプログラムやで」と怒られる。
今回の OS(Raspbian GNU/Linux 10)には、Python 2.7.16 と Python 3.7.3 が共存している。

そこで「じゃあ」と、Python 3 で実行すると、

pi@raspberrypi:~/test/pasori/nfcpy/examples $ sudo python3 tagtool.py
Traceback (most recent call last):
  File "tagtool.py", line 29, in <module>
    import ndef
ModuleNotFoundError: No module named 'ndef'

ndef モジュールが無いと怒られる。

20190816_raspbian1.jpg
どうすりゃいいの???(^^;

しばし考えたが、ndef モジュールを Python 3 用にセットアップするのが一番近道だと思ったので、以下のとおり対応。

・RC-S380 が認識されているか確認(ま、念の為)

pi@raspberrypi:~ $ lsusb|grep Sony
Bus 001 Device 004: ID 054c:06c3 Sony Corp. RC-S380

・nfcpyは導入されているか?(一緒に導入される libusb1 を見てみる)

pi@raspberrypi:~ $ pip3 list|grep usb
pi@raspberrypi:~ $ pip list|grep usb
libusb1           1.7.1

Python 2.7.16 の方には入っているのに、Python 3.7.3 の方には入ってない。
Python 3.7.3 の pip(Python のサードパーティー製パッケージを管理するためのツール)を使ってインストールが必要。ちなみに、Python 3.7.3 用の pip は pip3 という名でインストールされている。

・Python 3.7.3 の方にも nfcpy を導入

pi@raspberrypi:~ $ sudo pip3 install nfcpy
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting nfcpy
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/7e/ed/3b3a00ada844f8a9229c8a79f460a1a13e870b767a1eb0ee0caf99740e9d/nfcpy-1.0.3-py3-none-any.whl (186kB)
    100% |████████████████████████████████| 194kB 794kB/s
Requirement already satisfied: pyserial in /usr/lib/python3/dist-packages (from nfcpy) (3.4)
Collecting libusb1 (from nfcpy)
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProtocolError('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))': /simple/libusb1/
  Downloading https://www.piwheels.org/simple/libusb1/libusb1-1.7.1-py3-none-any.whl (59kB)
    100% |████████████████████████████████| 61kB 96kB/s
Collecting pydes (from nfcpy)
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Downloading https://www.piwheels.org/simple/pydes/pyDes-2.0.1-py2.py3-none-any.whl
Collecting ndeflib (from nfcpy)
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/c9/80/bbc9a4818cd74807f914d225611cd724d8c0e56237b952a9a4aa6d583f5c/ndeflib-0.3.3-py2.py3-none-any.whl (56kB)
    100% |████████████████████████████████| 61kB 792kB/s
Installing collected packages: libusb1, pydes, ndeflib, nfcpy
Successfully installed libusb1-1.7.1 ndeflib-0.3.3 nfcpy-1.0.3 pydes-2.0.1

なんか、やたらと「Cache entry deserialization failed, entry ignored」と出てるが???最後は Successfully installed で終わってるんで良さ気。

・ちゃんと導入されたか見てみる

pi@raspberrypi:~/test/pasori $ pip3 list|grep usb
libusb1           1.7.1
pi@raspberrypi:~/test/pasori $ pip3 list|grep nf
nfcpy             1.0.3

大丈夫そう。

で、FeliCa カードを読むサンプルプログラムを実行してみる。

pi@raspberrypi:~/test/pasori/nfcpy/examples $ sudo python3 tagtool.py
[nfc.clf] searching for reader on path usb
[nfc.clf] using SONY RC-S380/P NFC Port-100 v1.11 at usb:001:004
** waiting for a tag **
Type3Tag 'FeliCa Lite-S (RC-S966)' ID=012E4CD257C6A091 PMM=00F1000000014300 SYS=88B4

お。ちゃんと、FeliCa カード読んで ID も捕まえてるやん。OK、OK。
4年前にも「InternetExplorer の馬鹿さ加減には本当にうんざり」ってエントリーで同じことを書いてた(笑)
マジで InternetExplorer は糞。

例えば、こういうスクリプト。

function disp_input_area2() {

var form = document.forms.form1; 

if (form.kaito.value == "希望する") {
document.getElementById('input_area2').style.display = 'block';
}
else {
document.getElementById('input_area2').style.display = 'none';
}

}

HTML はこうね。

<input type="radio" name="kaito" id="kaito" value="希望する" onclick="disp_input_area2();" >希望する<br>
<input type="radio" name="kaito" id="kaito" value="希望しない" onclick="disp_input_area2();" >希望しない

これは、Chrome でも  Firefox でも(つまり InternetExplorer(以下 IE) 以外では)ちゃんと実行される。

form で、「回答を希望する」というラジオボタンを選んだら名前やメールアドレスを入力する部分(<div id="input_area2">で囲まれた部分とか)がパクっと開くという動きをするわけだ。

ただし、IE では動かない。

IE では、チェックされた radio ボタンの value 値を取れないからだ。undefined になる。

糞である。

なので、value 値を使う代わりに、radio ボタンの checked な状態を全部調べる必要がある。
だから、例えば以下のようになっちゃう。

function disp_input_area2() {

var form = document.forms.form1; 
var kaito = form.kaito;
var flg = "no";

for (var i = 0; i < kaito.length; i++) {
if (kaito[i].checked == true) {
if (i == 0) {
flg = "yes";
}
}
}

if (flg == "yes") {
document.getElementById('input_area2').style.display = 'block';
}
else {
document.getElementById('input_area2').style.display = 'none';
}

}

糞だ・・・
value 値で判断するほうが自然だしソースもシンプルである。

時々、なんでか知らんけどブラウザの中で IE が一番好きとかいう変態技術者に出会うこともあるんだけど、賢い人ならやっぱり IE は捨てていかないと。

前にも書いたけど、例えば社内 Web システムの実行ブラウザとして IE を指定している情報管理部門やメーカーって怠慢だと思うのよね。

はっきり言って、IE に対応するためだけに余計な工数がかかるんだから。(ま、そこで何度も保守工数が発生することがメーカーとしては狙いなのかもしれんけどね(笑))

このアーカイブについて

このページには、2019年8月以降に書かれたブログ記事のうちプログラミングカテゴリに属しているものが含まれています。

前のアーカイブはプログラミング: 2019年7月です。

次のアーカイブはプログラミング: 2019年9月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。


月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら