Doctrine オブジェクトレベルのトランザクション

はじめに

廣田です。東京は2月あたりのこの時期が一番寒いですね。(;´T`)ズズッ

この間、絶版した雑誌の記事がどうしても読みたくて国会図書館オンラインを初めて利用しました。
日本で出版された書籍を全て所蔵しているともいわれる国立国会図書館ですが、行かなくてもオンラインで記事(複写)を購入できるようになっているんですね。(知らなかった…)

使ってみた感想ですが図書館のサイトと侮るなかれ。
まるで大手通販サイトなみの使い勝手の良さに驚きました。
記事を「カートに入れる」んだぁ…💡

図書館らしいシンプルなデザインでありながら、わかりやすい操作性は個人的に大変勉強になるものでした。

創刊当初の週刊少年漫画なども所蔵しているので、機会があれば利用してみてはいかがでしょうか。
https://ndlonline.ndl.go.jp


さて今回はsymfonyで利用されているORMのDoctrineについて、
トランザクション中で、persistされたエンティティってflushされるまでどこに貯まるんだろうと疑問に思い調べたので、備忘としてメモしようと思います。

背景

以下のような感じでCSVファイルからデータを取り込んでDB登録している機能を作っていました。


 ・・・・
$file = $uploadForm->getData()['file'];
/** @var EntityManagerInterface $em */
$em->beginTransaction();
$errors = $csv->upload($file->getFileInfo(), $csvHeader, $callback);
     //↑ここで1行1行エンティティに変換してpersistしていたりする。
if (count($errors)) {
                $em->rollback();
                $this->addFlash('warning', 'エラーが発生したため登録していません。');
            } else {
                $em->commit();
                $this->addFlash('notice', '登録しました。');
            }
   ・・・・

CSVデータに内容がダブっている行があった時にそこはpersistしないようにしたいなぁ…
⇒ すでにpersistされているエンティティと今見ているCSVデータの内容を比較したいなぁ…
⇒ 同一トランザクション中ですでにpersistされているエンティティってどうやって取得すればいいんだろう??
というきっかけでした。

UnitOfWorkのサイクル

Doctrine公式のドキュメントによると、UnitOfWorkはオブジェクトレベルのトランザクションのようなものと表現されています。
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#working-with-objects

EntityManagerが生成された時、あるいはEntityManager->flushを発動した後に新しくUnitOfWOrkのサイクルが開始されるのだそう。
そのサイクルの中で変更をトラッキングし、データベースに正しい順序で書き込む役割を担う…とのことで
ここを見ればpersistされたエンティティを取得できそうと考えました。

UnitOfWorkへのアクセス

公式ドキュメントでは$em->getUnitOfWork();でUnitOfWorkにアクセスできるとしか書いてなかったので、 UnitOfWOrk配下の関数を一通り見ていると、それっぽい名前のものを発見。

$em->getUnitOfWork()->getScheduledEntityInsertions();//←これ


さっそく使ってみると、persistされているエンティティの一覧を配列で返してくれました。

他にもgetScheduledEntityUpdates()、cheduledEntityDeletions()などがあり、UnitOfWorkがトラッキングしているエンティティを状態ごとに取得できるようになっています。

さいごに

Doctrineってこういうライフサイクルなんだなーと、ほんっっっの少しだけ仕組みが理解できたような気がしました。

DLL+EXEからDLLの使い方を解析する[後編]

はじめに

イメージ・マジックの安藤です。
今年は雪が降ることはないのかなと思っていたらまさかこの時期にとは思いもしませんでした。ともあれ、平日でなくて良かったと思っています。私は地元が北海道なのですが、そちらは今年も順調に雪が積もっているようでした。
今回は前回の話の続きです。

あらすじ

前回はコンパイル時にDLL内のメソッドを暗黙的にリンクするまでの手順を説明しました。今回はメソッドの引数と戻り値の解析です。

.NETアプリケーションを逆コンパイルする

JavaやC#などコンパイル時に中間言語に翻訳されるような言語は、特殊な対策が施されていない限りかなり可読性の高い状態で逆コンパイルすることが可能です。 .NETアプリケーションを逆コンパイルするソフトはいくつかありますが、今回はILSpyというソフトを使用しました。

データのマーシャリング

逆コンパイルしたソースからDLLのメソッドの使い方がわかれば万々歳で全てが解決するかと言われるとそんなことはありません。.NET Frameworkにはマネージコードという概念があり、その外にあるコード(アンマネージコード)を呼び出すにはデータのマーシャリング(変換)が必要になります。
つまり、C++で使われると型とC#で使われる型はかなり違うということです。
大体の変換対応表はMSの公式ドキュメントに記載されています。
この中でも特にマルチバイトの文字列はC++で扱うのはだいぶ辛いです。それから、上記のドキュメントには書いていませんが、関数ポインタを渡したいときはdelegateが使われたり、参照(例えばWIN32APIにあるHANDLE型の参照はLPHANDLE)を渡したいときはrefキーワードが使われたりします。ちなみに、System.IntPtrとref System.IntPtrは別物です。

おわりに

やってみて思いましたがDLLの解析はノーヒントだとだいぶ辛いですね(探してもあまり情報が出なかったので)。なのでその軌跡をここに残すことにしました。

イメージ・マジックで今一番アツいレンズマウントは?

矢野です。去年(2018年)から、フルサイズなミラーレスカメラが非常に盛り上がっていますね。これまで私は機動性重視で小型軽量なマイクロフォーサーズを愛用してきましたが、これだけにわかに盛り上がってくると、多少大きくても(マイクロフォーサーズもいつの間にかずいぶん大きいのが多くなってきたし)フルサイズについつい目移りしてしまうものです(お値段はがもうちょっとどうにかなっていただきたいものではありますが……)。個人的には、Lマウントのシグマのカメラがどんななのかが非常に気になります。 では、我らがイメージ・マジックで人気のカメラはどんなものなんでしょう? 今回は、社内のごくごく一部(?)で非常に盛り上がっているレンズマウントをご紹介しましょう。Sマウントです!

Sマウントとは

「Sマウント」と言っても、戦後から1950年代ごろまで製造されたニコンのレンズマウントのことではありません。そっちはそっちで面白そうですが、脱線は我慢。工業用とか、監視カメラ用の小型の規格です。「M12マウント」(内径が12mm)とも呼ばれるようです。
Sマウント用レンズ
レンズは単焦点です。側面に螺子が切ってあって、これの締め具合でフォーカスの調整をします。特にストッパーなども無く、奥までねじ込んじゃうとセンサーと多分ぶつかるのでちょっとコワいです。
Sマウント対応のカメラモジュール
同じSマウントに準拠していればレンズやモジュールの交換は可能ですから、カメラの用途や対象物との距離などによって適宜使い分けることもできます。

Sマウントのカメラで撮影する

レンズを取り付けたカメラモジュール(PCにはUSBケーブルで接続)
レンズのマウントの形式は、当然ですがカメラ制御そのものにはあまり影響しません。UVC(USB Video Class)に対応したカメラモジュールであれば、PCに接続するだけで基本的には使用できるようになります。WindowsならMedia FoundationやDirectShow、その他の画像を扱うライブラリ等からアクセス可能です。カメラモジュール独自の機能を使いたい場合は、カメラ専用のAPIを呼び出すことになります。 というわけで、「使うだけ」ならさほど難しくないSマウントのカメラですが、これをどう使うか、価値を生み出すように使えるかが私たちの腕の見せ所です。

PHPコードのチューニング

はじめに

こんにちは、イメージ・マジックのもあいです。

最近はサッカーを見るときには2つのチームがどのように動こうとしているのかを観察することによって、より面白く観戦できるようになってきました。日本代表チームがアジアカップの決勝でどのような戦い方をするのかが楽しみではありますが、相手チームであるカタールにも気をつけてみるとさらに面白くなります。

今回はPHP(Symfony4)のパフォーマンスチューニングについての記事になります。

記事の前提
 OS: Ubuntu 18.04
 PHP:7.2.10
 nginx:1.14.0
 PhpStorm:2018.3.3
 ※全てのソフトウェアはaptコマンドでインストールしています

Xdebugのプロファイラ機能

PHPで関数/メソッドの処理時間を測定するのにXdebugをインストールします。

sudo apt install php-xdebug

php.iniを編集して有効にします。
/etc/php/7.2/fpm/conf.d/20-xdebug.ini は下記の通り

xdebug.remote_enable=On
xdebug.remote_autostart=On
xdebug.remote_host=192.168.33.1
xdebug.profiler_enable=On
xdebug.profiler_output_dir=/tmp
xdebug.profiler_enable_trigger = 1;
xdebug.max_nesting_level=1000
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_timeout=2000

ポイントとしては、xdebug.profiler_enableをOnにするのと、xdebug.profiler_output_dirに測定結果を出力するディレクトリを指定してphp-fpmをreloadします。測定が終わったら xdebug.profiler_enable を Offにしてphp-fpmをreloadします。常にOnにするのは処理が非常に重たくなるのでおすすめできません。

プロファイラを有効にして測定したいURLを実行すると下記のようなファイルが作成されます
-rwxrwxrwx 1 moai moai 42986908  1月 17 17:09 cachegrind.out.26276
-rwxrwxrwx 1 moai moai     2644  1月 17 17:09 cachegrind.out.26276.0c892b
この2つのファイルをPhpStormをインストールしているマシンにコピーします。

PhpStormでプロファイル結果を解析

PhpStormでプロファイル結果を読み込みます。

Tool → Analyze Xdebug Profiler Snapshot…

ファイル選択ダイアログが開かれるので、ファイルサイズの大きいファイルを選択します。
デフォルトで処理時間の多い順番に表示されますが、これだけだとわかりにくいので、Call Treeタブをクリックして実行順表示にしてから処理に時間がかかっているのをドリルダウンして探します。

遅い部分を探して比較を行うと下記のような状態にすることができます。
改修前
DlvryController->jsonが19657msかかっています。ここで全体の53%の処理時間がかかってル事がわかります。ローカルの仮想マシンで測定しているとはいえ遅すぎます。
jsonが何を行っているかというと、Symfony4のEntityをjsonに変換しています。フレームワークの処理で遅くなっているのであれば手の施しようが無いような気もしますが、よくよく調べるとクライアント側で使っていない情報も返却していました。送る情報が少なくなればそれなりに速くなるのでは無いかという課程のもと、返却する情報を必要最低限の情報に絞った改修を行いました。

プロファイル結果は下記の通り。
改修後
3603msになりました。4.5倍速くなったことになります。
ここに出てくる処理時間はプロファイルを行っているため実際よりも大分遅いですし、VirtualBox上の仮想マシンで実行していますので、本番環境よりもさらに遅くなっていますが、どこで遅くなったかを把握するにはちょうど良い事になります。

最後に

プロファイルを行うことによりどこが遅いのかが一目瞭然でわかったのと、使っていないデータまで送ると遅くなる事がはっきりしました。

開発をしていると、面倒だからといってデータ取得の便利メソッドを使ってデータを取得し、そのままクライアントに返却することをやってしまうのですが、そういう事をすると最後にしっぺ返しが返ってくる良い例でした。

Seleniumテスト環境設定時の注意点

はじめに

株式会社イメージ・マジックの技術ブログ、今週の担当のsoenoです。

二週間以上前に買うと決めたものを未だに買えていません。
日常生活においてはどうでもいいことこそ、タスク管理が重要なのかもしれません。

さて、今回はSeleniumのテスト環境の設定時に気を付けることを書こうと思います。


Seleniumとは。

webアプリケーション向けのテスト実行ツールです。
GUIを通した操作を自動で行わせることができます。

名前にまつわる注意点

Selenium~といった名前が結構いろいろ出てきます。
細かく書いていくとかえってやこしくなるので実例は省略します。

調べた結果が何かおかしいといったときは手元のSelenium~と参照している資料のSeleniumが同じものかを確認するといいかもしれません。(バージョンなど)

selenium builder

「GUIから操作してテストを書き出せる。」というものだったらしいのですが、
今はもうないです。

同様の機能を提供するSelenium IDEというプラグインもありますが
2019年1月の時点でスクリプトの書き出し機能はいません。

Selenium IDE は書き出しはできないとはいえ、自動化時にどのように動くかや、
web Driverで使用するターゲット名の確認ができる点で有用だとおもいます。
記録と再生のみならばこれだけでも十分かもしれません。

PhantomJS

非表示で実行されるブラウザ、非表示軽量のため本などでseleniumとセットで使われているのですがこちらもメンテナンス終了とのこと。
https://groups.google.com/forum/m/#!topic/phantomjs/9aI5d-LDuNE

Headless Chromeに切り替えるか、Headless FireFoxにするか、
もしくはメンテナンス終了ということを考慮しつつphantomjsを使うか等の判断がいります。

その他

環境設定時にプラグイン間のバージョンが整う必要があります。
バージョンがおかしいというエラーが出たら解消します。

久しぶりに触ると忘れていたりするのがヘッドレスブラウザの起動
テストの前にwebDriverTypeとして指定したブラウザを起動します。

最後に

GUIのテストは日々更新されるブラウザを対象とします。
環境にもまだまだ変化がありそうです。
その時に今回は待ったことで再びはまらないように気を付けたいと思います。

WordPressのセキュリティ

こんにちは、岡野です。
最近、当社で使用しているWordPressについてセキュリティ向上を図る案件があったので紹介します。

まずOWASPに載っている以下の様な事を実施しました。

・allow_url_fopenの禁止
この設定はファイル名にURLを指定できなくする物です。

・disable_functionsで、コマンド実行系(exec()など)の関数を禁止
この設定はOSコマンドインジェクションの防御には結構有効で、後述のAppArmorと組み合わせるとより効果的です。

・WP Security Audit Logプラグインをインストールし、操作履歴を保存
これは何か問題が生じた際の調査に使用します。

また、あらかじめ指定したPHPファイル以外はWebアクセスできない様にしています。

その他、FPMからDB接続ができなくなるため現在調整中ですがAppArmorも設定予定です。

WordPressもスマホアプリの様にプラグインやテーマ毎に細かな権限設定ができればと思いますが、PHP自体にその様な機能が無いため難しいかもしれません。

大量の情報から必要なものを探す(メール編)

こんにちは。イメージマジック三浦です。
昨年、月に1~2回やっているフットサル用に、長袖のトレーニングウェアを買いました。フットサルは屋外でやることが多いのですが、肌が直接外気に触れないだけで体の冷え具合が全く違うので、寒い時期の運動にはありがたい存在です。

今回はメールの中から必要な情報を取り出す、ということをテーマにして投稿します。

【システムからの通知メール】

弊社で運用しているシステムからは、日々多くのメールが送信されてきます。
多くは定時のバッチやタスクが「異常なし」で動作したことを通知してくるメールですが、 時々異常を通知するメールが送信されることがあります。
異常を感知した場合は、内容を確認して必要な対応を取らなくてはいけません。

通知メールは1通ずつ見ていくのが大変なくらいに送信されてきますが、 異常がない時に「異常なし」と通知してもらうことも重要です。「問題なし」の通知を止めてしまうと、異常がなくて通知がないのか何らかの異常で通知できないのか判断しにくくなります。

そこで、送信されてきたメールの中からエラーを通知するメールを取り出すことを考えます。

【キーワードの選定】

ここではシステムからの通知メール本文に
「error: x」(x = 1から9の整数)
という文字列があれば、何かしらの異常があるものと判断します。
異常が1件でも10件でも、「error: 1」という文字列で検索できるということです。

【どうやって抽出するか】

正規表現による検索が使えるメーラーの場合、 上記の規則性をカバーできる正規表現で条件を設定すれば簡単です。 しかし、検索に正規表現を使えないメーラーの場合は悩みます。
※普段の業務ではgmailを利用していますが、gmailは正規表現が使えません。

【実際にやったこと】

gmailでは、必要な分だけOR条件をつなげる方法が使えました。
“error: 1” OR “error: 2” OR … (9までつなげる)
検索条件のテキストを別ファイルに保存しておいて、gmailフィルタのラベル添付機能と組み合わせると、使いやすい状態になりました。

【まとめ】

今回は、大量の情報から必要な情報を探すという観点で書きました。
目の前にある情報をどう整理するかという観点と、どういう情報があれば整理しやすいかという観点の両方を忘れないようにしたいと思います。

axiosで渡したパラメータをSymfonyで受け取る方法まとめ

はじめに

あっという間に年の瀬で、もうすぐ入社から10か月の廣田です。

入社時よりは多少知識はつきましたが、他の記事を読むとハイレベルだな~、まだまだだな~と感じ入ります。

とはいえ難しいことは語れないので(汗)、今回は毎回調べてしまうaxiosとSymfonyのやりとり(SymfonyのRequestクラスの使い方)について書こうと思います。

開発本部で使っているjavascriptフレームワーク、Vue.jsでサーバーとやり取りする際にaxiosを利用していますが、その際のaxiosの書き方やSymfonyでパラメータを受け取る時の方法が、いつもなんだっけ?となるので備忘がてらまとめました。


Routeにplaceholderを設定しているとき

SymfonyでRouteを以下の例のように設定している場合、GET/POST送信されたパラメータをコントローラの引数として取得できます。

単純な値を1、2個渡すだけならこの方法を使ったほうが良いと思います。(URLの見た目もスッキリします。)
▼javascript側
const url = Routing.generate('app_find_prdct', {id: this.id});
const res = await axios.get(url);
if (!res.data.success) {
   EventBus.$emit('simple-modal_alert', 'エラー', res.data.errorMessages, callback);
   return;
}
//うまくいったときの処理
・・・
  ▼PHP側
  /**
     * @Route("/findProduct/{id}", name="app_find_prdct", options={"expose"=true}, methods={"GET"})
     * @param ProductRepository $pRepo
     * @param int|null $id
     * @return JsonResponse
     */
    public function findProduct(ProductRepository $pRepo, ?int $id ): JsonResponse
    {
        $p = $pRepo->findById( $id );
        if( !$p ){
            return $this->json(['success' => false]);
        }
        return $this->json([
            'success'  => true,
            'product' => $p
        ]);
    }
Routeでプレースホルダーに{id}とすることでコントローラの引数$idでaxiosが渡したパラメータを取得しています。

SymfonyのRequestクラスを使う

プレースホルダーの指定をしないときはSymfonyのRequestクラスを使います。

use Symfony\Component\HttpFoundation\Request;
public funtion someFunction(Request $request) {
     ...............
//GETの時
$id = $request->query->get('id');
//POSTの時
$id = $request->get('id');

//JSON形式でPOSTされた値を取得する時など
$content = $request->getContent();
['id' => $id] = json_decode($content, true);

}
axiosで渡すパラメータの値がJSON形式の時や、パラメータが複数の時はRequestクラスのgetContentを使うと、POST/GETされたそのままのデータにアクセスできます。
さらに、その値をPHPのjson_decodeを利用して連想配列形式で取得したりできるので便利です。

※参考:Symfony Routing
    generate_url_javascript
    HttpFoundation Component

さいごに

初歩的なところですが、当然のように使っていたので文章にまとめてみると理解が深まったような気がします。
記事を書く順番が来るとネタを決めるまで悩みますが、振り返る良い機会になりますね^^

冬休みまで指折り数える毎日ですが、それまで体調管理に気を付けて頑張りましょう!
ではでは。

DLL+EXEからDLLの使い方を解析する[前編]

はじめに

イメージ・マジックの安藤です。
先日から季節が急速に真冬の様相を呈していますね。風邪やインフルエンザが流行りだす時期なので皆様におかれましても十分ご用心ください。ちなみに、私は去年の大晦日から今年の三が日にかけてインフルエンザを発症するという何とも言えない新年スタートを切っているので気を付けます。
今回は題名の通りDLLの解析をしたという話です。

DLLとは

多分この説明要らないんじゃないかなぁと思いつつ書いておきます。
DLLとは、プログラムのある機能を再利用できるようにプログラム本体と切り分けたライブラリのことを指します。Windowsならば.dll、Linux系では.soという拡張子が使われます。
なお、今回の話はWindowsの.dllで、一般的な、Cで記述されたDLLという話です。

コンパイルを通すのに必要なもの

DLLの利用方法には暗黙的リンクと明示的リンクが存在します。今回は完全に個人的にDLLを使いたいだけなので暗黙的リンクでコンパイルするものとします。
以下が必要になります。

  • インポートライブラリ(.lib)
  • ヘッダファイル(.h)
更にメタ的に言うとこうなります。

  • エクスポートされている関数名
  • 関数の引数と型

初期状態

DLLがどんな状態で使われていたのかを少しだけ書いておきます。
DLLとDLLの機能をラップしたEXEがあり、EXEに引数を渡して実行してあげるとDLLの機能が使えるという構成になっていました。最終的にはこのEXEを介さずにDLLの機能を呼び出すのがゴールです。

インポートライブラリを用意する

第一の関門として、インポートライブラリが存在していません。ただ、これに関してはVisual Studioを使えばDLLから同等のものを用意することができます。
まず、以下のコマンドを実行します。

dumpbin /exports hoge.dll


C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools>dumpbin /exports VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll
Microsoft (R) COFF/PE Dumper Version 14.16.27024.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll

File Type: DLL

  Section contains the following exports for c1.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 000DDDB0 AbortCompilerPass
          2    1 000DDE50 CloseTypeServerPDB
          3    2 000055FC InvokeCompilerPassW

  Summary

       32000 .data
       17000 .pdata
       F3000 .rdata
        7000 .reloc
        1000 .rsrc
      138000 .text

このコマンドでDLLからエクスポートされている関数の一覧がテーブル上に表示されます。MSVCに存在する適当なDLLを表示してみます。 ここでordinal~nameの列に注目すると関数名が分かります。この関数名を以下のように羅列し、拡張子を.defで保存します。これはモジュール定義ファイルと呼ばれるものです。

EXPORTS
AbortCompilerPass
CloseTypeServerPDB
InvokeCompilerPassW

作成したモジュール定義ファイルは以下のコマンドでインポートライブラリに変換できます。なお、64bitアーキテクチャを想定しています。

lib /DEF:hoge.def /MACHINE:X64 /out:hoge.lib

ヘッダファイルを用意する

ヘッダファイルでは以下のように書くことでDLLの関数を宣言できます。

#define DLLImport __declspec(dllimport)

#ifdef __cplusplus
extern "C"{
#endif

    DLLImport void funcName1();
    DLLImport int funcName2(int arg);

#ifdef __cplusplus
}
#endif


以上を見るとわかりますが、関数の戻り値と引数がわからないとDLLの機能は使えないことが分かります。

以降、今回のケースで関数定義をどう解析したかは次回の私の番で書いていきたいと思います。

「ブラックマーク」は縁の下の力持ち

はじめに

矢野です。小石川オフィスに引っ越してきて、1か月あまりが経ちました。小石川や、それに近い西片・本郷界隈は起伏が激しくて入り組んだ道が多く、引っ越し直後はランチの後にちょっと散歩するとすぐに迷子になっていたものです。それでもだんだんと脳内の地図が整理されてきて、かなり歩きやすくなってきました。

ブラックマークって何?

ちょっとググってみると、「ドリフト走行などで路面にできるタイヤの黒い跡」とか「汚点」とか、今年公開のそういうアクション映画があるとか、色々出てきます。ですが、私たちが扱っているブラックマークとは、「印刷用紙の裏側の黒い印」のことです。

ブラックマーク

ブラックマークの例。用紙を横切るような帯状のものや、紙面の中央に四角く描かれたものなど。

生産管理に使用する小さなラベルや完成品をお届けするための配送伝票などのシールの台紙となる剥離紙の裏側に印刷されている黒い印や帯のことです。シールとして貼る際に捨てられてしまうので、お客様の目に触れることはありません。いわば縁の下の力持ちですね。

何のために使うの?

私たちが取り扱うラベルプリンターには印字ヘッドの近くに光学式のセンサーが内蔵されていて、それで台紙の裏側をスキャンできます。ブラックマークを検知するまでは用紙を送り出して、検知したところで位置合わせ。そこから印刷を始めて、1枚分印刷できたら台紙ごとカットする(これもプリンターに内蔵されたカッターがやってくれます)。1枚ごとの長さが決まっているロール式のラベルを正確に取り扱うための、重要なマークです。

と、ある程度取り扱えるようになってから言うのは簡単ですが、プリンターと用紙がそれぞれ専用の組み合わせでない場合は、用紙の向き、サイズ、光学センサーの位置、検知の方式(透過式? 反射式?)、マークからの相対的な位置の設定などなど、いろいろな苦闘がありました。用紙のフィードがいつまでも止まらなかったり、おかしなところでちょん切られたり……。

形のないソフトウェアの世界と、プリンターやRFIDリーダーなどのハードウェア、印刷メディアなどの間を行ったり来たりしながら、私たちは開発の仕事をしています。