
最近C#の勉強をしていて、Visual Studioの使い方でわからないことがあります。わからなくて調べたちょっとしたことも忘備録として残していこうと思います。 このようにコードの上に「○○個の参照」のような文字が表示さ … 続きを読む
最近C#の勉強をしていて、Visual Studioの使い方でわからないことがあります。わからなくて調べたちょっとしたことも忘備録として残していこうと思います。 このようにコードの上に「○○個の参照」のような文字が表示さ … 続きを読む
今日ブログのレイアウトを大幅に変更した。変更するに当たりいきなりネット上で変更するのは怖かったので一旦ローカル環境にうつしてからアップロードする作戦にしたら、Wordpressのインポートでいきなり苦労したので内容を報告します。
以前ローカルに”local”で環境を作り、All-in-One WP Migrationを使用して、バックアップしPHPのバージョンアップしたことがあった。その時はインポート容量に制限があることがわからなかったが、今回改めてやってみると300MBの制限があった。
容量の上げ方はこのサイトに書いてあった。
how-to-increase-maximum-upload-file-size-in-wordpress/
① Edit .htaccess file
php_value upload_max_filesize 128M
php_value post_max_size 128M
php_value memory_limit 256M
php_value max_execution_time 300
php_value max_input_time 300
② Edit wp-config.php file
@ini_set( 'upload_max_filesize' , '128M' );
@ini_set( 'post_max_size', '128M');
@ini_set( 'memory_limit', '256M' );
@ini_set( 'max_execution_time', '300' );
@ini_set( 'max_input_time', '300' );
local だとどちらの方法もダメだった。①の場合、エラーでサイトが開かなくなる。②の場合、変化なし。
直接php.iniを書き換えてもダメ。
結局localでやるのは諦めた。localはWordpressの環境を作るのが簡単なので気に入ってたのに。
MAMPでWordpressの環境を作った。MAMPの場合、アップロードの初期値は8MB。①の方法で変更できた。
.htaccess
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /wp/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wp/index.php [L]
</IfModule>
# END WordPress
# ここから下を追加
php_value upload_max_filesize 1000M
php_value post_max_size 1000M
php_value memory_limit 1000M
php_value max_execution_time 300
php_value max_input_time 300
②の方法は変化なかった。
WordPressのバックアップの仕方とローカルサイトでの試し方などは、このサイトが非常に良かったので紹介しておく。
WordPressのPHPを安全にバージョンアップする方法
WordPressのバックアップをかんたん確実に取る方法【初心者向け】
WordPressをローカル環境(パソコン)にインストールする方法
今の仕事で、サーボドライバに外部速度設定器(ボリューム)を2つ取り付けて制御しましたが、意外と苦労したので報告します。
ボリュームによる速度司令は、電圧を-10V〜+10Vの範囲で設定し速度を統制します。この装置は正転時にボリューム1を使用し、逆転時にボリューム2を使用するように切替えて制御しています。
電圧が0Vより大きいときは正転し、0Vより小さいときは逆転します。厳密に言うとLG端子(グランド)より、VC端子(電圧入力)の電圧が大きいか小さいかで回転方向が変わります。
下記の図のようにLG端子よりVC端子が大きければ正転方向に回転します。
また、下記図のようにLG端子よりVC端子が小さければ正転方向に回転します。
さて今回使用するドライバは、サーボドライバ内にボリューム駆動用の電源が搭載しているタイプでした。そこで同じような考えで回路を組んだら、逆転方向が動作しませんでした。
この回路ではVCとLGが同電位になってしまいます。LGを+12Vに接続するわけにもいきません。(ショートしてしまうので)
ボリュームにドライバ内部電源の0〜12Vをかけて、ボリュームと指令信号1/2をを切り替えるようにしました。
更新がかなり久しぶりになりました。約二年ぶりです。この間に単身赴任になったり、長期で出張に行ったりといろいろありました。単身赴任はまだ継続中ですが。
タイトルをTry Everythingに変更しました。自分が興味あることや仕事に関係することについて出来ることをどんどんやっていこうという想いです。
単身赴任中ということもあり、DIYや電子工作など部材が必要なことはあまりやらなくなりました。最近の興味はもっぱら資格取得です。
私は電気に関する仕事をしているので、とりあえず電気工事士二種をとりました。電験三種も受けましたが、基準点が下がれば受かるかもしれません。
この先は英語や中国語の勉強をしていくつもりです。
森沢洋介著「瞬間英作文」。素晴らしいですね。英語で会話するときに言いたいことがあるけれど、文章にできずに簡単にしか言えませんでしたが。この本をきっかけに打開できてきたような気がします。付録CDをiPhoneに入れて、ミュージックアプリで通勤途中に聞いています。日本語を聞いて、英語の文章を作って、英語の音声と比べてみる。というのを繰り返して、長い文書も段々と自然と作れるようになってきました。この学習も次の段階で、話すための瞬間英作文シャッフルトレーニングを買ってやろうとしています。
前置きは長くなりましたが、今回は瞬間英作文シャッフルトレーニングのCDをMP3にしてiPhoneへ転送したところで、意外な苦労をしたので書きます。
何故かアルバム名がたくさんできてしまいました。アルバム名を揃えたり短くしたりいろいろやってみましたがうまくいきませんでした。結果的に、いったんmp3ファイルを1つだけ転送してから、残りのファイルを転送するとうまくいきました。下記のブログにずばり答えが書いてあったので助かりました。
iPadのMusicアプリ上でアルバムがまとまらない
またMacでMP3のアルバム名やタイトルなどをGUIで設定したい場合はID3 Editorが利用できます。アルバム名やジャンルなど共通の項目は複数のファイルをまとめて編集することができます。
これは曲名をすべて半角に変更して対応しました。ID3 Editorで曲名は一つずつ編集するしか無いのでコマンドラインですることを考えました。ID3 Editorでもコマンドラインのツールが有るようですが、やり方があまり詳しく乗っていないので、今回はpythonでライブラリを読み込んでやることにしました。まず作成したコードは
mutagenというライブラリを使用しています。ライブラリのインストールは
pip install mutagen
全角半角の変換はjaconvライブラリを使用しています。これもライブラリのインストールが必要です。
pip install jaconv
.replace(‘Part’,”)でPartという文字を消していますが、これは、CD2のタイトルがPart1と2に分かれていて、これがあるとiPhoneのミュージックアプリで見たときはみ出してしまうので削除しました。編集した結果下記のように見やすくなりました。
前回まで三菱MCプロトコルのライブラリをつくるため通信テストをしてきましたが、プロクラムの中にASIOの記述をずらずらと書いていたのでクラスにまとめてモジュール化しようと考えています。
クラスにするだけでも、結構苦労しました。特にsocketをローカル変数宣言して、コンストラクタで設定するところです。構造体のデータを転送してみましたが、エラーが発生してうまく行かなかったので、まずはnetcatで立ち上げたローカルサーバへの送信プログラムを作ってみました。プログラムはgistへアップしましたので参照してください。
UDPClient
早速説明していきます。UDPClientクラスの変数はendpointとsocketを宣言します。コンストラクタではio_contextとIPアドレスとポート番号を受けて、socketとendpointを初期化しています。
class UDPClient
{
private:
asio::ip::udp::endpoint endpoint;
asio::ip::udp::socket socket;
public:
UDPClient(
asio::io_context& io_contex,
const std::string host,
const short port)
: socket(io_contex)
{
asio::ip::udp::endpoint endpoint_(asio::ip::address::from_string(host), port);
endpoint = endpoint_;
}
UDP送信は構造体で送信することを考えて、テンプレートで作成しました。(まだ実際に構造体での転送は試していません。)
template <typename T>
void Send(T msg,int size)
{
socket.send_to(asio::buffer(&msg, size, endpoint);
}
mainの中は次のとおりです。io_contexとIPアドレスとポート番号を指定してクラスを宣言し、キーボードで入力した文字をUDPサーバへ送信するだけのプログラムです。
asio::io_context io_context;
const std::string host = "127.0.0.1";
const short port = (short)60000;
UDPClient udp(io_context,host,port);
udp.Open();
for(;;)
{
std::string inmsg;
getline(std::cin, inmsg);
if(inmsg=="q") break;
inmsg+="\r\n\r\n";
udp.Send(inmsg,inmsg.size());
}
udp.Close();
私のMacbook環境ではnetcatが使えるのでnetcatが使えるので、netcatをUDPサーバ、ポート番号60000で立ち上げて、文字が表示することを確認しました。
$ netcat -l -u -p 60000
メインのプログラムでinmsg+=”\r\n\r\n”;としているのは、netcatでうまく開業されなかったからです。他に1文字か2文字だと一段多く改行したり、十何文字か多く打つと文字が変になったりするのですが、とりあえずモジュール化して通信できるました。
次は受信についてやり、構造体のデータを送ります。
前回に続き、今回は構造体でコマンドを設定しての受信について説明します。
まずは受信の構造体からです。
//受信コマンドヘッダ
struct QNa3E_Frame_Receive{
std::uint16_t subheader;
std::uint8_t network_number;
std::uint8_t pc_number;
std::uint16_t request_IO_number;
std::uint8_t request_station_number;
std::uint16_t response_data_length;
std::uint16_t terminal_code;
}rsv;
これも、リファレンスマニュアルのQnA互換3Eフレームのバイナリコードで更新する場合を見ながら作成しました。UDP受信については前々回の三菱PLCとイーサネット通信をご参照ください。
// UDP受信
std::array<unsigned char, 128> recv_buf;
asio::ip::udp::endpoint sender_endpoint;
size_t recv_buf_len = socket.receive_from(
asio::buffer(recv_buf), sender_endpoint);
今回はこの受信バッファを構造体に入れます。
// 受信データを受信ヘッダへコピー
int rsv_size = sizeof(QNa3E_Frame_Receive);
memcpy(&rsv, &recv_buf,rsv_size);
構造体のサイズ分memcpyでコピーしています。
// 受信したデータを書き出し
for(size_t i=rsv_size; i<recv_buf_len; i+=2)
std::cout << (recv_buf[i + 1] << 8) + recv_buf[i] << std::endl;
受信したデータを標準出力で出力します。受信バッファrecv_bufには1バイトずつデータが入っているので、1ワードずつ出力するには、forループで2つずつ進めて上位バイトを8bitシフトさせて下位バイトを足して、ワードデータにして表示しました。出力は下記のようになります。
1
10
100
構造体で作成することができました。次回からはMX Componentの関数にあるような感じでクラスを作っていきます。
前回の記事三菱PLCとイーサネット通信では、文字列に書いたコマンドを送信して受信データを文字列で表示しましたが、今回はそれを構造体にして書いてみます。
コードはまたgistsにアップします。
melsec_struct_test.cpp
それではコードを説明します。まずは送信コマンドのヘッダの構造体です。
#pragma pack(push,1)
//送信コマンドヘッダ
struct QNa3E_Frame_Send{
std::uint16_t subheader=0x0050;
std::uint8_t network_number=0x00;
std::uint8_t pc_number=0xFF;
std::uint16_t request_IO_number=0x03FF;
std::uint8_t request_station_number=0x00;
std::uint16_t request_data_length;
std::uint16_t cpu_monitoring_timer=0x10;
} qna;
pragma pack(push,1)を入れることにより、バイナリで転送したときの間を開けずに詰める事ができます。もし入れない場合は「std::uint8_t request_station_number=0x00; //要求先ユニット局番号」の後に00が入ってしまいます。構造体宣言の最後にはpragma pack(pop)と書きます。
構造体はリファレンスマニュアルのQnA互換3Eフレームのバイナリコードで更新する場合を見ながら作成しました。
次に一括読み出しの構造体です。
//一括読み出しコマンド
struct ReadAllCommand{
std::uint16_t command=0x0401;
std::uint16_t sub_command=0x0000;
std::uint32_t top_device;
std::uint16_t number_of_device;
} read_all;
デバイスはDが0xA8、Mが0x90と値が決められているので列挙体で作成しました。std c++11からは列挙体の基礎となる型を指定できます。
//デバイス
enum struct Device_type : std::uint32_t{
D = 0xA8,
X = 0x9C,
Y = 0x9D,
M = 0x90,
};
次にmain関数に移ります。まずはデバイス番号を設定します。前回の例と一緒でD300から3ワードという設定です。
/デバイス番号を設定
std::uint32_t device = static_cast<std::uint32_t>(Device_type::D);
列挙体ではキャストして変数へ格納します。
read_all.top_device = ( device << 24) | 300; // D300
read_all.number_of_device = 3; // 3ワード
デバイスは4バイトのうち先頭の1バイトがDとかMなどの種類で、残り3バイトがアドレスになります。列挙体で格納したデータを3バイト分(24bit)シフトしました。デバイス数はそのまま数値を入力します。
qna.request_data_length=sizeof(qna.cpu_monitoring_timer) + sizeof(read_all)
で、要求データ長を計算しています。
次にUDP送信です。
// UDP送信
std::string const host = "192.168.0.10";
short const port = 1025;
asio::io_context context;
asio::ip::udp::endpoint endpoint(
asio::ip::address::from_string(host), port);
asio::ip::udp::socket socket(context);
socket.open(asio::ip::udp::v4());
socket.send_to(asio::buffer(&qna, sizeof(qna)), endpoint);
socket.send_to(asio::buffer(&read_all, sizeof(read_all)), endpoint);
ASIOについては前回の記事を参照してください。send_toで送信するところは、送信ヘッダと受信コマンドを構造体毎に送っています。
レスポンスの受信については次回説明します。
またまた久しぶりの更新になってしまいました。今回のテーマは三菱PLCとイーサネット通信。将来的にはRaspberry Piなどの非WindowsのからPLCへアクセスしたいと考えています。
まずは通信の確認。Perlで通信確認をしているブログがあるのでこれを参考にしました。Raspberry Piで動作させるならPerlでも十分なのですが、マイコンでも動作させることを考えて、C++で作成しました。
このサイトを参考ししています。
Perlで三菱シーケンサーとSocket通信出来上がったプログラムはGistに載せました。
melsec_test.cppやっていることは、UDP接続でD300~D302のデータメモリを受信をリクエストして、返答を受信するという内容です。基本的に参考にしたサイトの内容をC++に置き換えているだけですが、通信方式はUDPにしたところが違います。
環境ですが、MacbookにUSB接続優先LANでPLCと接続しています。UDP通信にはAsio C++ Libraryを使っています。Asioはhppファイルのみで構成されているので、ファイルをダウンロードして、適当なフォルダに移して、コンパイル時にインクルードするだけで使用できます。コンパイルはこのような感じに。
clang++ -std=c++11 -stdlib=libc++ -g -O0 -W -I../../source/asio-1.12.2/include melsec_test.cpp -o melsec_test
プログラムの内容について説明していくと
Boostに依存したくない場合はインクルードする前に、ASIO_STANDALONEマクロを実行する必要があるとのこと。
#define ASIO_STANDALONE
#include <iostream>
#include <asio.hpp>
テストなのでMCプロトコルの受信コマンドを文字列で定義します。コマンドの詳細については「Perlで三菱シーケンサーとSocket通信」を確認してください。
// 送信コマンド
std::string cmd = "500000FFFF03000C001000010400002C0100A80300";
std::cout << "send: " << cmd << std::endl
array配列を作って、文字列をバイナリデータに変換しています。perlだとpack関数があるようですが、ここでは自分で作りました。(もしC++に便利なライブラリがあるのなら教えていただきたいです。)
//16進数アスキーを数値に変換
char hex2char(char h)
{
if (h >= '0' && h <= '9')
return h - '0';
else if (h >= 'A' && h <= 'F')
return h - 'A' + 10;
else if (h >= 'a' && h <= 'f')
return h - 'a' + 10;
return 0;
}
文字を一文字ずつ数値に変換して、char一つについて2つの数値が入るように詰めて配列に入れています。16進数の文字列を0〜15に変換する関数も作りました。
// 送信コマンドをバイナリデータへ変換
std::array<unsigned char, 128> send_buf;
int cnt;
for (size_t i = 0; i < cmd.size(); i += 2)
{
cnt = i / 2;
send_buf[cnt] = (hex2char(cmd[i]) << 4) + hex2char(cmd[i + 1]);
;
}
int send_buf_len = cnt + 1;
ASIOの送信部分です。非同期I/Oオブジェクトを作って、送信先のアドレスとポートをエンドポイントに設定し、ソケットを開き、データをバッファに入れて送信。という流れです。ASIOについてはあまり理解していないので引き続き勉強が必要です。
// UDP送信
std::string const host = "192.168.0.10";
short const port = 1025;
asio::io_context context;
asio::ip::udp::endpoint endpoint(
asio::ip::address::from_string(host), port);
asio::ip::udp::socket socket(context);
socket.open(asio::ip::udp::v4());
s
ちなみにTCP送信のときは次のようにすればできます。(PLC側にTCPポートを1026に登録)
// TCP送信
std::string const host = "192.168.0.10";
short const port = 1026;
asio::io_context context;
asio::ip::tcp::endpoint endpoint(
asio::ip::address::from_string(host), port);
asio::ip::tcp::socket socket(context);
asio::connect(socket,&endpoint);
socket.write_some(asio::buffer(send_buf, send_buf_len));
次にUDPからの受信。送信とは逆に配列を作って、受信バッファから配列に入れます。
// UDP受信
std::array<unsigned char, 128> recv_buf;
asio::ip::udp::endpoint sender_endpoint;
size_t recv_buf_len = socket.receive_from(
asio::buffer(recv_buf), sender_endpoint);
最後に受信したバイナリデータを文字列に変えていきます。Perlではunpack関数があるのですが、ここでも自作しました。配列を一つずつ見ていきcharの上位と下位をそれぞれ文字列に変換します。
// 受信バイナリデータを文字列に変換
std::string result = "";
for (size_t i = 0; i < recv_buf_len; i++)
{
result += char2hex((recv_buf[i] >> 4) & 0xf);
result += char2hex(recv_buf[i] & 0xf);
}
数値を文字にする関数は下記のとおりです。
//0から15までの数値をアスキーに変換
char char2hex(char h)
{
if (h >= 0 && h <= 9)
return h + '0';
else if (h >= 10 && h <= 15)
return h - 10 + 'A';
return 0;
}
実行結果は下記の通り。Perlと同じ結果になりました。
send: 500000FFFF03000C001000010400002C0100A80300
rev : D00000FFFF03000800000001000A006400
次回以降、コマンドの構造体を作り、ライブラリで使えるようにしていきたいです。
タイトルにある通り、Qt5とOpenCV4を使ったアプリを作りたいのですが、なかなか思うように進んでいません。両方とも初心者なので入門書から勉強しているところです。ここ2・3ヶ月はOpenCV4基本プログラミングと本で勉強していました。Qt5はYoutubeのチュートリアル動画を使って勉強しています。
一通り勉強してなんとなくわかってきたので、試しにアプリを作ってみました。OpenCVのVideoCaptureで動画を取り込んで、手動でカーソルを合わせるアプリ。顕微鏡にカメラを取り付けて、表示することを想定しています。更新速度が遅かったり、クラッシュしたりと本当に使い物になりませんが、作成するにあたってわかったことを忘備録として書きます。
ボタンを押して、VideoCaptureを開始し、動画を連続で表示させようとしたら他の操作ができません。スレッドの出番です。標準ライブラリにtreadがあるので、実装して実行したらクラッシュ。わけがわかりませんが、ネットで色々調べると、Qt で使えないとのこと。領域が被る?まだよく理解できてませんが、QThreadを使って実装することができました。
Qtではsignal、slot、connectでイベントを設定しますが、参考するサイトによって書き方がかなり違うので戸惑ってしまします。下記の2つの書き方があるようです。
①connect(mThread,SIGNAL(NumberChanged(int)),this,SLOT(onNumberChanged(int)));
②connect(mThread, &MyThread::NumberChanged, this, &Dialog::onNumberChanged);
基本は connect(送り元オブジェクト, 送り元の関数, 送り先オブジェクト, 送り先の関数)の関数です。
①はSIGNALやSLOTという命令を使っています。引数は(int)のよう型だけを書くのがポイント。②はクラス名から書き、引数は書きません。
ネット上に乗っているOpenCVの情報はまだまだ古いものがあり、そのままでは今のバージョンでエラーになることがあります。OpenCVはBGR、QtはRGBの色配列の並びが違うので変換する必要があります。OpenCVで色を変換する場合はcvtColorで色を入れ替えますが、その引数で書かれているCV_BGR2GRAYはCOLOR_BGR2RGBとしなければなりません。QImageに変換するにプログラムは下記の通り。
video >> frame;
cv::cvtColor(frame,frame,cv::COLOR_BGR2RGB);
QImage qimgin (frame.data,
frame.cols,
frame.rows,
static_cast<int>(frame.step),
QImage::Format_RGB888);
今回のアプリでは動画の更新が1秒間隔になってます。連続にするとクラッシュしてしまうからです。また、カーソルと連続で移動すると同じくクラッシュしてしまします。使い物になりません。まだまだ、QtやOpenCVを使えているというレベルになっていないので、もう少しサンプルを見ながら勉強して出直すつもりです。とりあえずソースはアップしました。