使用C++Builder Berlin Update 2開發BeaconFence 應用程式

BeaconFence在數個C++Builder/Delphi版本中就推出了, 我記得也在數年前的產品發表會中介紹過,當時Beaconfence身價不菲, 我也沒機會用. 但從Seattle版本後Embarcadero大幅降低 Beaconfence的價格, 而且提供開發人員版,所以才有機會試用它.

 

日前一位使用C++Builder的朋友向我抱怨Beaconfence只有Delphi的範例, 沒有C++Builder的範例, 他問我到底能不能用C++Builder開發Beaconfence? 能不能幫忙用C++Builder做一個Beaconfence的POC? 因此筆者特別商借了3個Beacon, 一個是插電式, 2個使用電池的Beacon:

upload1

upload2

upload3

藉由TBeaconMapFencing元件載入POC地點的佈建架構圖, ,再分別把3個Beacon設置在書房, 客廳和玄關處:

upload4

Berlin Update 2版Beaconfence我最喜歡的一點就是為TBeaconMapFencing元件加入了Extended Mode, 這個模式可以同時掃瞄iBeacon,AltBeacon和Eddystone 3種不同型式的Beacon, 又方便而且又穩定:

upload5

之後我們就可以藉由下面的C++程式碼根據使用者的喜好設定TBeaconMapFencing元件的地圖設定值:

void TfmMainForm::ChangeOption(TFencingMapOption AOption, bool ShouldInclude)

{

  TFencingMapOptions LOp = BeaconMapFencing1->MapOptions;

  if (ShouldInclude)

  {

  LOp << AOption;

  }

  else

  {

  LOp >> AOption;

  }

 

  BeaconMapFencing1->MapOptions = LOp;

}

最後實作TBeaconMapFencing元件的On PositionEstimated事件處理函式:

void __fastcall TfmMainForm::BeaconMapFencing1PositionEstimated(TPointF &AEstimatedPoint, TPointF &APointToPath)

{

  ShowStatus(AEstimatedPoint, APointToPath);

}

 

void TfmMainForm::ShowStatus(TPointF AEstimatedPoint, TPointF APathPoint)

{

  String LMapsInfo = “";

 

  for (int I = 0; I <  BeaconMapFencing1->ProjectInformation.Maps.Length; I++)

  {

  LMapsInfo = LMapsInfo + " Map:" + IntToStr(I) + " Act:" + IntToStr(BeaconMapFencing1->ProjectInformation.Maps[I].GetActiveBeaconsCount() );

  if (BeaconMapFencing1->ProjectInformation.Maps[I].NearestBeacon().BTBeacon != NULL)

    LMapsInfo = LMapsInfo + " dis:" + FormatFloat(“0.00″, BeaconMapFencing1->ProjectInformation.Maps[I].NearestBeacon().BTBeacon->Distance);

  }

 

  LbStatus->Text = IntToStr(GTimes) + " at " +

  FormatFloat(“0.00″, AEstimatedPoint.X) + " , " + FormatFloat(“0.00″, AEstimatedPoint.Y) +

    " (" + FormatFloat(“0.00″, APathPoint.X) + " , " + FormatFloat(“0.00″, APathPoint.Y) + “)" +

  " " + LMapsInfo;

  GTimes++;

}

把此範例App部署到筆者的HTC手機中執行並且在POC地點中行走時就可以看到此App能精確的在室入定位筆者的位置, 例如下圖是筆者在客廳中移動, 在接近設置在客廳的Beacon設備時BeaconFence的Beacon便變成綠色, 代表筆者就在此Beacon設備附近0.5公尺之內:

upload6

下圖則顯示筆者移動到書房時也能立刻且精準的定位筆者:

upload7

本文說明了使用C++Builder不但能完全沒問題的開發BeaconFence的應用, 而且充滿了樂趣.

廣告

2 則迴響

使用Berlin Update 2的TCalendarView, TCalendarPicker和Livebindings

Berlin Update 2的新功能之一就是提供了2個新的VCL元件:

TCalendarView和TCalendarPicker

這2個元件可以讓開發人員藉由它們的Date特性值來處理日期資料:

snap1

不過這2個元件也仍然可以藉由Livebindings和資料庫中的欄位資料繫結,開發人員可以右擊滑鼠選擇Bind Visually:

snap2

於Live Bindings視窗中再點選元件下方的”…”啟動Bindable Memebers對話盒, 再於其中選擇要繫結Date特性:

snap3

最後再拖曳資料庫的日期欄位到此2個元件的Date特性即可. 例如下圖就是筆者在TCalendarView和TCalendarPicker人件中繫結和顯示MS SQL Server 2012中Northwind資料庫Employees資料表的BirthDate欄位資料:

snap4

TCalendarView和TCalendarPicker元件可以結合程式碼或是Live Bindings技術處理資料, 仍然保持和其他VCL元件一樣的直覺和好用的特性.

發表留言

柏林版的DataSnap

在發表RAD Studio柏林版介紹到DataSnap功能時,英巴的文件只說柏林版的DataSnap除了改bug之外,就是開始移除Indy並改用THTTPClient等新的HTTP相關類別, 另外又隱約提到一個類別TDBXJSONStream,它可使用在DataSnap服務方法中. 第1次看到TDBXJSONStream時感覺有點陌生,但從它的名稱可大約猜出它應該是一個使用在dbExpress(DBX?)應用中的相關類別並提供JSON串列流的功能(JSONStream?).

嗯, 我很好奇的在IDE中搜尋TDBXJSONStream,並在Data.DBXCommon程式單元中找到了它, TDBXJSONStream是一個新類別,我並沒有在以前的版本中看到它.查看它的程式碼發現它提供了JSON Reader和Writer, 那麼這代表它提了寫入JSON物件和讀取JSON物件的功能.

property Reader: TJSONReader read GetJSONReader;

property Writer: TJSONWriter read GetJSONWriter;

此外它又有一個TStream物件特性, OK, 那麼它一定可以在此TStream物件中寫入和讀出JSON物件, 而這些功能也正是它的名稱代表的意義.

property BaseStream: TStream read FBaseStream;

瞭解了它的功能之後我們就可以來看看如何使用它. 先建立一個DataSnap Server專案, 在其中定義一個QueryHotel方法, 請注意QueryHotel方法回傳的物件正是TDBXJSONStream:

function QueryHotel(const sName : String) : TDBXJSONStream;

此DataSnap Server專案使用FireDAC存取MS SQL Sevrer中的資料:

Snap2

QueryHotel方法展示了如何使用TDBXJSONStream物件, 要寫入JSON物件到TDBXJSONStream, 我們只需要使用它的Writer特性取得它的JSON Writer:

function TServerMethods1.QueryHotel(const sName: String): TDBXJSONStream;

var

  sSQL : String;

 

  procedure WritePair(const sProperty, sFieldName : String);

  begin

    Result.Writer.WritePropertyName(sProperty);

    Result.Writer.WriteValue(qryTheHotel.FieldByName(sFieldName).AsString);

  end;

begin

  Result := TDBXJSONStream.Create;

  sSQL := qryTheHotel.SQL.Text;

  sSQL := sSQL + “" + sName + ‘%"‘;

  qryTheHotel.SQL.Text := sSQL;

  qryTheHotel.Open();

  try

    Result.Writer.WriteStartArray;

    while (not qryTheHotel.Eof) do

    begin

      try

        Result.Writer.WriteStartObject;

        WritePair(‘名稱’, ‘STITLE’);

        WritePair(‘地址’, ‘ADDRESS’);

        WritePair(‘電話’, ‘MEMO_TEL’);

      finally

        Result.Writer.WriteEndObject;

        qryTheHotel.Next;

      end;

    end;

  finally

    Result.Writer.WriteEndArray;

    Result.Writer.Close;

    qryTheHotel.Close;

  end;

end;

編譯並執行此DataSnap Server之後就可以使用瀏覽器來試著呼叫QueryHotel方法, 下圖顯示成功的呼叫QueryHotel方法並取得由TDBXJSONStream物件回傳的JSON資料:

Snap1

當然我們也可以使用Windows/OSX/Android/iOS客戶端來呼叫QueryHotel方法.

下面的顯示了如何在客戶端呼叫QueryHotel方法, 取得TDBXJSONStream物件並藉由TDBXJSONStream物件中的JSONReader物件讀出在伺服端寫入的JSON資料:

procedure TForm5.Button1Click(Sender: TObject);

var

  aServer : TServerMethods1Client;

  js : TDBXJSONStream;

begin

  aServer := TServerMethods1Client.Create(Self.SQLConnection1.DBXConnection);

  try

    js := aServer.QueryHotel(Edit1.Text);

    js.Reader.Rewind;

    while (js.Reader.Read) do

    begin

      case js.Reader.TokenType of

        TJsonToken.StartArray:

        begin

          while (js.Reader.Read) do

          begin

            case js.Reader.TokenType of

              TJsonToken.PropertyName:

              begin

                ListView1.Items.Add.Text := js.Reader.Value.ToString + ‘ : ‘ + js.Reader.ReadAsString;

              end;

            end;

          end;

        end;

      end;

    end;

  finally

    aServer.Free;

  end;

end;

執行客戶端並開始查詢資料就可以看到下面的結果畫面:

Snap4

那麼為什麼要使用TDBXJSONStream類別呢? 我想方便是主因, 因為它已經把數個功能結合在一個類別中, 免除程式師自己使用多個類別. 另外我在英巴文件中找到的資料說的是TDBXJSONStream類別執行速度快. 嗯,我並沒有去測試這個說明的真正的意義, 但它真的很好用就是了.

Have Fun!

 

9 則迴響

RAD Server簡介網路研討會

昨天QCOM錄影了RAD Server簡介網路研討會並且上傳到了YouTube, 有興趣的朋友可以去看看, 並且再次抱歉昨天一開始時硬體設備的問題.

下面是昨天使用的範例, 下載後把檔案結尾的.PDF去除, 因為這個檔案是一個ZIP檔.

0630RADServer.ZIP

影片錄影在: https://www.youtube.com/watch?v=vtIiOKKD5qM

 

發表留言

柏林版如何進化程式碼

Metric和Audits這2個功能早已經存在Delphi很多年了, Metric和Audits可以讓我們檢查和保證程式碼的撰寫品質, 但就我個人所知經常使用Metric和Audits的開發人員卻很少, 我也不知道為什麼.

在我於各地進行Delphi相關的活動中,也認識了一些朋友, 他們可能是專案/產品經理, 因為他們通常都會詢問一些類似的問題, 例如:

  1. Delphi有沒有工具確保外包程式碼的品質?
  2. Delphi有沒有工具確定外包廠商有遵守我們的制定的程式碼規範?
  3. 如何尋找程式碼中的漏洞和弱點?
  4. 等等…

等問題. 事實上Metric和Audits就可以幫助開發人員確保程式碼的品質, 在柏林版中又加入了Toxicity功能可以幫助開發人員制定和搜尋程式碼規範. 例如我看過許多公司都規定一個函式長度不能超過多少行, 一個if敘述不能超過多少層, 以及函式的參數不能超過多少個等.

這些程式碼規範有的和日後維護的難易有關, 有的和執行效率有關, 都是非常實用的規範, 但在數萬或是數十萬行的程式碼中要如何驗證巨量的程式碼呢?

在柏林版中開發人員可以在IDE中點選Tools|Options|Toxicity Metrics啟動Toxicity的設定對話盒:

Snap1

在Toxicity Metrics設定對話盒你可以看到我們可設定驗證函式長度, 函式參數數目, if敘述層, 以及函式需要測試的獨立執行路徑數目等重要的程式碼品質數據.

接著你可以開啟Delphi專案, 在IDE右上方的搜尋欄中輸入Toxicity, 就可以看到Toxicity命令:

Snap4

執行它, 它就可以開始對你的Delphi專案進行驗證, 並顯示結果:

Snap2

不合驗證規範的函式就會以紅色標誌, 使用滑鼠雙擊紅色標誌項目IDE就會開啟對應的程式碼. 例如上面的函式DeductAmount是一個非常複雜的函式, 它使用的if敘述層有6個, Toxicity功能可以即時又精確的在數萬行的程式碼中幫助我們定位到這個函式:

Snap3

接著我們就可以再次檢驗DeductAmount函式是否真的需要如此複雜, 可不可以藉由重構簡化它呢?

善加使用Audits/Metrics和Toxicity功能可以不斷的完善和提昇程式碼品質, 當然也可以幫你驗證他人或是外包的程式碼. Have Fun!

 

 

 

 

 

1 則迴響

柏林版的隱藏功能

還記得在Delphi 3的時代有一本相當有名的書籍” Hidden Paths of Delphi 3: Experts, Wizards and the Open Tools API”,它討論了當時Delphi 3一些鮮為人知的功能, 非常的有趣. 不過我今天並不是要著墨這本書, 而是因為柏林版推出後大多數為人熟知的新功能就是就的安裝程式, IoT的新功能以及新的C++編譯器.不過柏林版也有一些人們忽略的好康, 就像是隱藏版的功能一樣, 今天就讓我們看看有些什麼看不見的好東西.

我有個多年的習慣就是在安裝新版的Rad Studio之前都會把前一版的原始程式保留下來並和新版的原始程式比較, 研究之用. 在安裝完柏林版之後就可以很方便的啟動Beyond Compare來比較, 研究有興趣的程式碼:

Snap2

例如柏林版提到了StrReplace和一些其他功能改善, 然而言語之間很模糊,我也不太清楚真正的改善是什麼, 因此我就使用Beyond Compare直接來比較, 發現柏林版許多的RTL都有改寫的跡象, 例如下圖就是使用Beyond Compare來比較StrReplace:

Snap1

而發現了巨大的改變. 如果我們在IDE中搜尋StrReplace在RTL/VCL/FMX中使用的情形可以發現StrReplace是一個熱點函式, 也就是說StrReplace是被大量使用的函式:

Snap3

而且String的helper class也大量的使用了StrReplace:

function TStringHelper.Replace(OldChar, NewChar: Char): string;

begin

  Result := System.SysUtils.StringReplace(Self, OldChar, NewChar, [rfReplaceAll]);

end;

 

function TStringHelper.Replace(OldChar: Char; NewChar: Char; ReplaceFlags: TReplaceFlags): string;

begin

  Result := System.SysUtils.StringReplace(Self, OldChar, NewChar, ReplaceFlags);

end;

 

function TStringHelper.Replace(const OldValue, NewValue: string): string;

begin

  Result := System.SysUtils.StringReplace(Self, OldValue, NewValue, [rfReplaceAll]);

end;

嗯, 這代表如果柏林版改善了StringReplace函式那麼就代表幾乎所有有使用到字串處理的程式都會得到助益, 但我們並不知StringReplace到底改善了多少? 柏林版的StringReplace到底有沒有比以前版本的StringReplace好呢?

要找答案很簡單, 寫一個大量使用StringReplace的範例程式比較一下不就得了?如果只是改善3~5%, 那就…..

下面是同樣的程式, 只是一個用西雅圖版編譯, 一個用柏林版編譯.這支程式在一個公開的電子書中呼叫StringReplace來取代字串, 下面執行的結果可能會令人大吃一驚, 因為柏林版的StringReplace比西雅圖版快上數百倍, 令人印象深刻:

Snap4

嗯, 柏林版的這個隱藏功能太棒了, 當你處理字串資料的應用程式使用柏林版重新編譯之後您有沒有發現執行速度似乎變快了呢? Have Fun!

 

 

2 則迴響

Wow, 太酷了, Delphi XE5可以做Google Glass的開發!

今天一早看到下面的內容:

 

http://delphi.org/?post=1371

 

Delphi XE5現在已經可以開發Google Glass的App, 雖然只是最簡單的App, 但已經證明Delphi開發穿戴式設備的App是沒有問題的, 這也展開了Delphi進入穿戴式設備開發的序幕了.

 

希望明年也有機會用用Google Glass, 現在只能在白日夢中幻想擁有Google Glass.

3 則迴響