« 2017年1月 | トップページ | 2017年3月 »

2017.02.28

Lazaru+FPCとDelphi10.1 Berlinの実行ファイルサイズ

Lazarus+FPCで作成したWindows実行ファイルのサイズは1,732kbあるのですが、Delphi10.1 Berlinではどうかと思いBerlinでWindows実行ファイルを作成してみました。まずはLazarusプロジェクトフォルダーのコピーを作成してコピーしたプロジェクトソースを修正していきます。
・フォームファイルUnit1.lfmの拡張子をUnit1.dfmに変更する。
・Unit1.pasソースの{$R *.lfm}を{$R *.dfm}に変更する。
・Project.lprの拡張子をProject.dprに変更する。
・DelphiでProject.dprを開く。この時フォーム読み込み時にDelphiとLazarusでコンポーネントの名前が違っていたりプロパティが違っている箇所でエラーが出るので、フォームのソースファイルを開いてエラーが出た箇所を力づくで修正する。
・エラーが無くなったらプロジェクトを保存して一旦閉じる。
・Project.dprojファイルをエディタで開いて


の行を見つけ、もしここが
となっていたら、以下のように修正する。

 
   
Form1

   dfm
 

 
となっていると、メインフォームがない状態となり、フォームデザイナーでフォームの編集が出来ないようです。

で、何とかDelphi 10.1 Berlinで正常にコンパイル出来るようにして作成した実行ファイルのサイズは・・・2,328kb・・・Lazarus+FPCよりも大きなサイズとなりました。昔のDelphiの頃にはLazarus+FPCのファイルサイズが大きいことが弱点だったのですが、ファイルサイズでは今や逆転してしまったようです。
しかしながら、Lazarus+FPCではCOMなどのWindows特有の機能を使用したアプリケーション開発が出来ないようですので、目的に合わせて使い分けるのが良いかもしれません。

Berlinで作成した実行ファイル
Publishberlin


| | コメント (3) | トラックバック (0)

2017.02.18

Raspberry piでLazarus1.7+FPC3.0

Raspberry piでapt-getを使用してインストールしたLazarusのバージョンは1.2.4+FPC2.6.4ですが、手動で最新版をインストールしてみました。
getlazarus.orgからRaspberry pi用のセットアップスクリプト(setup.sh)をダウンロードして、シェルから

chmod +x setup.sh
./setup.sh

を実行します。
必要なライブラリが不足している場合は、apt-get install xxxxxxのメッセージが出ますので、メッセージにしたがって不足しているライブラリをインストールして、あらためてsetup.shを実行すればLazarus1.7+FPC3.0をインストール出来ます。

で、その最新版がこちら

フォームデザイナ
Laz17frc

ソースコードエディタ
Laz17src

うーん...最近のDelphiやVisual Studioと同じようなデザインに変わっています...
まぁ他がこの流れなので、操作性のユニバーサル化という意味では正しい流れなんでしょうね。これはこれで使い勝手は良いので、いいんじゃないでしょうか。

で、こちらがWindows版の1.7です
Laz17win

| | コメント (0) | トラックバック (0)

2017.02.16

Raspberry piでLazarusを使ってIoT

Raspberry pi/RaspbianでのC#/mono実行環境ではデータ入力と送信のサイクル限界が200ms程度とあまりよろしくありませんでした。Raspbian上のmonoが遅いのか、MCP3208アクセスライブラリが遅いのか原因はよくわかりこれはませんが、何とか処理スピードを上げられないかと、ネイティブコンパイラであるLazarusを使用してみました。
ADコンバータMCP3208用のライブラリは、トランジスタ技術のダウンロードサービスから2014年7月号の「特集 第6章 HDMIテレビにグラフ表示! 8チャネル・ロング・メモリ・データ・ロガー」をダウンロードして、この中のspi_mcp3208.pasを使用させていただきました。
また、MQTTライブラリにはmqtt-free-pascal-masterを使用しています。

尚、そのままではLaspbian上でコンパイルした実行ファイルがMQTTに接続する時点で、Semaphore init failed (possibly too many concurrent threads)エラーが出て停止してしまうため、プロジェクトソースに以下の変更を加えています(Web上で見つけた解決策です)。

program publish;
 
{$mode objfpc}{$H+}
 
uses
  {$DEFINE UseCThreads} ←この一行を追加
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
 

Lazarusで作成したアプリケーションでは、データ入力と送信のサイクル限界が30msまで上がりました。この性能が出ると、50msサイクル程度までは安定した出力が保証できそうです。8チャンネル分の入力データをいちいちフォーム上に表示させていてこのスピードですから、さすがはネイティブコンパイラといった感じでしょうか。入力データの表示を行わなければ、もっとスピードが上がるかもしれません。

Delphi使いの私としては、Lazarusは言語仕様がDelphi互換でフォームデザインも同じ感覚で行うことが出来るので、使っていてまったく違和感がありません。また、LazarusはWindows上でも使用できるためフォームデザインから基本的なプログラミングまでをWindows上で行ってRaspbian上にプロジェクトをコピーして仕上げることが出来ます。Visual Studio/C#ではWindows上で作成した実行ファイルがRaspbian/mono上でそのまま動くという利点はありますが、GPIO処理などのRaspberry pi依存部分のデバッグは遅くて貧弱なRaspbian上のMonoDevelopで行う必要がありますので、Lazarusの方が優位と行った感じでしょうか。

Rasplaz

Lazarus/Raspbianでの主な処理は以下の通りです。

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ExtCtrls, Spin, DateUtils, IniFiles, MQTT, spi_mcp3208;

// スタート
procedure TForm1.StartBtnClick(Sender: TObject);
var
URL: string;
i: integer;
begin
Topic := PTopic.Text;
URL := BURL.Text;
MQTTClient := TMQTTClient.Create(URL, 1883);
MQTTClient.Connect;
Sleep(500);
if MQTTClient.isConnected then
begin
IdleTimer1.Interval := Interval.Value;
IdleTimer1.Enabled:= True;
StopBtn.Enabled:= True;
StartBtn.Enabled:= False;
BURL.Enabled := False;
PTopic.Enabled:= False;
for i := 1 to 8 do
CB[i].Enabled:= False;
Pdt := 0;
end else begin
MQTTClient.ForceDisconnect;
MQTTClient.Free;
MessageDlg('MQTT connection was failed.', mtError, [mbOK], 0);
end;
end;

// データ入力・Publish処理
procedure TForm1.IdleTimer1Timer(Sender: TObject);
var
InData: array[0..7] of single;
i, tgap: integer;
tdt: TDateTime;
pub, tmp: ansistring;
begin
// 出力フォーマット
// YYYY/MM/DD h:min:sec.msec, interval, data1, data2, .... ,data8
// intervalは前回データ取得から今回までの間の時間(msec)
tdt := Now;
if Pdt > 0 then
tgap := MilliSecondsBetween(Pdt, tdt) //前回から今回までの時間をmsecで取得
else
tgap := 0;
Pdt := tdt;
DateTimeToString(pub, 'yyyy/mm/dd hh:nn:ss.z', tdt);
tmp := IntToStr(tgap);
pub := pub + ',' + tmp;
ADC8.Read(InData);
for i:= 1 to 8 do
begin
if CB[i].Checked then
begin
tmp := FloatToStr(InData[i-1]);
if MonitorStat.Checked then
DT[i].Text:= tmp;
pub := pub + ',' + tmp;
end else
pub := pub + ',';
end;
MQTTClient.Publish(Topic, pub);
end;

| | コメント (0) | トラックバック (0)

2017.02.05

Raspberry piでC#を使ってIoT

「Outlookアドインを作成する」の記事でRaspberry piでIoTの実験うんぬんの話をしていましたが、さわりをまとめてみます。
Raspberry piにmonoをインストールして.NET環境を整えて、テストプログラムはVisual stdio 2015 communityで行って作成した実行ファイルをRaspberry piに転送して実行します。

Windows7上の実行状況
Win7screen

上記の実行ファイルをそのままRaspberry piで実行
Raspiscreen

といった感じです。
RaspberryのIO入力を処理するため、Windows上では基本動作の確認だけを行い、問題がないようであればRaspberry piにフォルダー毎コピーして動作テストを行っています。

このテストプログラムはADコンバータMPC3208の8チェンネルデータをタイマー時間毎に読み込んでMQTTブローカーにデータを流します。

#それにしてもRaspberry piでの処理時間が遅いこと・・・私の環境ではデータ読み込みサイクルが最短で200ms弱ですので、とてもリアルタイム入力とは言えません・・・

using System;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Windows.Forms;
using uPLibrary.Networking.M2Mqtt; // MQTTパッケージ (NuGet)
using uPLibrary.Networking.M2Mqtt.Messages;
using Raspberry.IO.GeneralPurpose; // GPIOアクセス用パッケージ
using Raspberry.IO.Components.Converters.Mcp3208; // ADC MCP3208用パッケージ
using Raspberry.IO;
 
 
namespace Publisher
{
    public partial class Mainform : Form
    {
        private MqttClient Client; // MQTTオブジェクト
        const ConnectorPin adcClock = ConnectorPin.P1Pin23; // MCP3208制御用定数宣言
        const ConnectorPin adcMiso = ConnectorPin.P1Pin21;
        const ConnectorPin adcMosi = ConnectorPin.P1Pin19;
        const ConnectorPin adcCs = ConnectorPin.P1Pin24;
        private GpioConnectionDriver driver;
        private Mcp3208SpiConnection adcConnection;
        private Mcp3208Channel chan1, chan2, chan3, chan4, chan5, chan6, chan7, chan8;
        private bool Mcp3208 = false;
        private DateTime PrevDT;
 
        public Mainform()
        {
            InitializeComponent();
        }
 
        private void Mainform_FormClosed(object sender, FormClosedEventArgs e)
        {
            if (Client != null)
            {
                Client.Disconnect();
                Client = null;
            }
            if (Mcp3208)
            {
                adcConnection.Close();
                adcConnection = null;
                driver = null;
            }
        }
 
        //チャンネルステータスの変更
        private void ON1_CheckedChanged(object sender, EventArgs e)
        {
            if (Stop_btn.Enabled) 
            {
                timer1.Enabled = CH1.Checked | CH2.Checked | CH3.Checked | CH4.Checked
                               | CH5.Checked | CH6.Checked | CH7.Checked | CH8.Checked;
            }
        }
 
        // ADC MCP3208からのデータ読み込み
        private string ReadMCP3208(Mcp3208Channel Ch)
        {
            AnalogValue p;
            decimal points;
            p = adcConnection.Read(Ch);
            // 一旦128bit精度の実数値にして、それを文字列に変換する
            points = p.Value;
            return points.ToString();
        }
        // インターバルタイマー割り込み
        private void timer1_Tick(object sender, EventArgs e)
        {
            string dat, data;
            DateTime dt;
            TimeSpan tgap;
            double mgap;
 
            // 今回読み出し時間と、前回読み出し時からの経過時間をミリ秒で取得する
            dt = DateTime.Now;
            tgap = dt - PrevDT;
            mgap = tgap.Seconds * 1000 + tgap.Milliseconds;
            PrevDT = dt;
            // 読み出し年月日時間, 経過時間, CH1, CH2, CH3, Ch4, CH5. CH6. CH7, CH8
            // のフォーマットで読み出しデータを文字列化していく
            data = dt.ToString("yyyy/MM/dd hh:mm:ss.fff") + "," + mgap.ToString() + ",";
            if (CH1.Checked)
            {
                dat = ReadMCP3208(chan1);
                Data1.Text = dat;
                data += dat;
            }
            data += ","; // カンマ区切り
            if (CH2.Checked)
            {
                dat = ReadMCP3208(chan2);
                Data2.Text = dat;
                data += dat;
            }
            // 以下CH8まで続く
            // :
            // 作成した8チャンネル分のデータ文字列をPublishする
            Client.Publish(Topic.Text, Encoding.UTF8.GetBytes(data), MqttMsgBase.QOS_LEVEL_AT_MOST_ONCE, false);
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {   // MCP3208ドライバを作成し接続する
                driver = new GpioConnectionDriver();        // GPIOドライバーオブジェクト
                adcConnection = new Mcp3208SpiConnection(   // MCP3208オブジェクト
                                    driver.Out(adcClock),
                                    driver.Out(adcCs),
                                    driver.In(adcMiso),
                                    driver.Out(adcMosi));
                chan1 = Mcp3208Channel.Channel0; // 8チャンネル分
                chan2 = Mcp3208Channel.Channel1;
                chan3 = Mcp3208Channel.Channel2;
                chan4 = Mcp3208Channel.Channel3;
                chan5 = Mcp3208Channel.Channel4;
                chan6 = Mcp3208Channel.Channel5;
                chan7 = Mcp3208Channel.Channel6;
                chan8 = Mcp3208Channel.Channel7;
                Mcp3208 = true;
            }
            catch
            {
                MessageBox.Show("Cannot open the SPI.", "Error", MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
                return;
            }
 
            // MQTTクライアントを作成
            Client = new MqttClient(BrokerIP.Text);
            string clientId = Guid.NewGuid().ToString();
            try
            {
                Client.Connect(clientId);
                timer1.Enabled = CH1.Checked | CH2.Checked | CH3.Checked | CH4.Checked
                               | CH5.Checked | CH6.Checked | CH7.Checked | CH8.Checked;
                Start_btn.Enabled = false;
                Stop_btn.Enabled = true;
            }
            catch
            {
                MessageBox.Show("Cannot connect to MQTT Broker.", "Error", MessageBoxButtons.OK,
                                MessageBoxIcon.Error);
            }
            PrevDT = DateTime.Now;
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                timer1.Enabled = false;
                Client.Disconnect();
                Client = null;
                driver = null;
                adcConnection.Close();
                adcConnection = null;
                Status.Text = "Status: OFF";
                Start_btn.Enabled = true;
                Stop_btn.Enabled = false;
            }
            catch
            {
                ;
            }
        }
    }
}

| | コメント (0) | トラックバック (0)

2017.02.03

Outlookアドインの改良?版

前の記事で紹介した添付ファイルを暗号化ZIPファイル化するOutlookアドインは、ZIP書庫内のファイルを開く際にパスワードの入力が必要ではあるものの、ファイル名自体は丸見えですので、第三者から見られたらどんなファイルが添付されているのかがわかってしまいます。
ということで、ファイル名を見られるのはいやだという場合を考えて、添付ファイルを一旦普通にZIP書庫に圧縮して、そのZIP書庫を暗号化ZIPファイルにすれば良いのではと思って修正したのが以下になります(修正箇所のみ)。
まぁここまでやる必要があるのかということなのですが、複数のファイルを格納する場合には、ファイルを開くたびにいちいちパスワードを入力しなければならないのですが、これだと中のZIPファイルを開く時に1回だけパスワードを入力すれば、その中にあるファイルには自由にアクセスできるので複数のファイルを添付する場合にはこちらの方が便利かもしれません。


       // 添付ファイルをまとめてパスワード付きZIPファイルに圧縮する
private void button2_Click(object sender, RibbonControlEventArgs e)
{
string originalFile, compressFile, displayName, wp;
int cnt, i;
wp = GetWorkPath() + "\\";
compressFile = wp + "Attachment.zip";
//普通に暗号化圧縮しただけでは元のファイル名がわかってしまうので
//一旦Attachment.zipに通常圧縮する
cnt = mailItem.Attachments.Count;
using (ZipFile zip = new ZipFile(Encoding.GetEncoding("Shift-JIS")))
{
zip.CompressionLevel = CompressionLevel.BestCompression;
for (i = cnt-1; i >= 0; i--)
{
Attachment attachFile = mailItem.Attachments[i + 1];
originalFile = wp + attachFile.FileName;
attachFile.SaveAsFile(originalFile);
zip.AddFile(originalFile, "");
attachFile.Delete();
}
zip.Save(compressFile);
azip = compressFile;
}
//圧縮したZIPファイルをパスワードZIPファイルにする
compressFile = wp + GetZipName();
using (ZipFile zip = new ZipFile(Encoding.GetEncoding("Shift-JIS")))
{
zip.CompressionLevel = CompressionLevel.BestCompression;
zip.Password = PassWord();
//ひとまとめにしたZIPファイルを更にZIP書庫に追加
zip.AddFile(azip, "");
zip.Save(compressFile);
}

「アドインセットアップファイル」をダウンロード

※既に以前のアドインをインストール済みの場合は、一旦コントロールパネルからアンインストールした上でインストールして下さい。
※ライセンス:個人的な使用のみ可です。

| | コメント (0) | トラックバック (0)

« 2017年1月 | トップページ | 2017年3月 »