太陽光発電の発電量をロギングしてみた。(6)Salesforceの容量不足に困ったら・・・BigObjectの活用

[【太陽光発電の発電量をロギングしてみた。目次】](https://qiita.com/takoyakiya/items/6b318ab83bcac86c966c)
### Salesforce Developer Editionの限界

[(4)Salesforseでリアルタイム監視](https://qiita.com/takoyakiya/items/7adbf0282b9ce9051bb7)で説明したが、Salesforce Developer Editionは、利用可能なデータストレージが5MByteしかない。
はじめ、電流値のロギングを10秒周期でアップロードしていたが、1日半で制限に引っかかり、アップロード不能になってしまった。
最も手っ取り早い対策方法は、アップロード周期を長めにとることである。

しかし、この制限を意識せずに利用する方法が存在する。
それは、BigObjectを活用することである。

### BigObjectとは？

主に、履歴やログを残すのに用いられるオブジェクトである。
Developer Editionでは100万レコードまで保持することが可能である。
BigObjectが通常のオブジェクトと異なるところは、

・ レポートやダッシュボードなど通常の機能で使用できない。
・ 通常のカスタムオブジェクトはAPIアクセス名に「\_\_c」がついたが、BigObjectは「__b」
・ リレーショナルデータベースでなく分散型データベース
・ 非トランザクションデータベース

詳細は以下を参照

【参照】
[Salesforce および Lightning プラットフォームのオブジェクトリファレンス](https://developer.salesforce.com/docs/atlas.ja-jp.object_reference.meta/object_reference/big_object.htm)

### どう活用できるの?

まず、手始めに、作成したカスタムオブジェクトと同等の項目定義をもつBigObjectを作成しておき、容量オーバーになる前にカスタムオブジェクトのデータをBigObjectにバックアップしてから削除することができる。

BigObjectに格納しても標準のレポート機能で使用できないのでグラフ表示できない！と思われるかもしれないが、ちょっと頑張れば、この問題をクリアする方法がある。
自分で作成するVisualforceページからはBigObjectにアクセス可能である。

### BigObjectの作成

「設定」-「管理」-「データ」-「BigObject」を選択する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/f22c40a8-b6e6-4420-7c40-c162ec8e117e.png)

「New」ボタンをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/72d0cbb9-c948-c2ba-d008-00b922412110.png)

表示ラベル・オブジェクト名を指定して「保存」する。

![SF10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/3ddb828b-d9b9-4335-6f94-609bc9ebd1cd.png)

カスタム項目＆リレーションで「新規」をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/8dbe6177-137e-9603-c767-a2274e4435e8.png)

データ型を選択して「次へ」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/5a3b5763-9bce-8a3a-d89f-51a987dd7b51.png)

この辺はカスタムオブジェクト作成時と同じである。
カスタムオブジェクトと同様に項目を作成すること。

ただし、数式項目は作成できないので注意する。

### カスタムオブジェクトからBigObjectへのデータバックアップ

ここでは、開発者コンソールを利用して作業を行う。
開発者コンソールは、「設定」同様、画面右上のギアボタンのメニューから起動できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/36949ebb-d0c6-da2a-69e0-6678c5a5e532.png)

「開発者コンソール」を選択すると、別ウィンドウで開発者コンソールが起動する。

開発者コンソールでは、SOQL (Salesforceで使用するSQL)を実行できる。

![SF11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/297f118a-d05e-e1b7-e275-968661efe883.png)

画面下のパネルの「Query Editor」にSOQLを入力し、一番下のExecuteをクリックする。すると、真ん中のパネルに実行結果が表示される。
これで、現在保持しているデータを確認できる。

では、データをカスタムオブジェクトから引き出して、BigObjectに格納するにはどうしたらよいか?
これは、普通にSOQLを発行するだけでは不可能である。
これには、Apexを使用する。

上部メニューから「Debug」-「Open Execute Anonymous Window」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/0fc8356b-054e-0701-c892-1e8b47218fca.png)

Enter Apex Codeウィンドウが起動するので、実行したいApexコードを入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/0556164b-3e9f-05da-a00e-e28add01b144.png)

サンプルコード

```Apex
List <GeneratorLog__c> lst =
 [SELECT Id,Name,Current__c,Voltage__c,Temperature__c FROM GeneratorLog__c WHERE MeasDate__c= TODAY AND hour__c=12] ;
 
if (lst.size() > 0 ) {
    
    for(GeneratorLog__c data : lst)
    {
        GeneratorLogStack__b entity = new GeneratorLogStack__b(
            No__c = data.Name,
            Date__c = data.Name,
            Current__c = data.Current__c,
            Voltage__c = data.Voltage__c,
            Temperature__c = data.Temperature__c
        );
        Database.insertImmediate(entity);
    }
    
 delete lst;
 system.debug('★' + lst.size()+ '件 削除しました★');
} else {
 system.debug('★該当データなし★');
}
```

入力したソースは、Executeボタンクリックで実行される。
ただし、一回の実行時間に制限がある。(10秒くらい)
大量にデータをコピー・削除しようとすると、途中で切られてしまうので、Where条件でデータを分割しながらデータ移行するとよい。

### Visualforceページの作成

バックアップしたデータも、やはりグラフ表示できると嬉しい。
これにはVisualforceページを使用する。

Visualforceページの作成は以下の手順で行う。

「設定」-「プラットフォームツール」-「カスタムコード」-「Visualforceページ」を選択。

![SF12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/878c35c2-bab2-f081-4bb5-94200aa105ab.png)

「新規」ボタンをクリック。
表示ラベルと名前を入力する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/8ba4fc03-0873-97ef-50e3-18c8de95e5e6.png)

デフォルトで数行コードが書かれており、そのまま保存してプレビューすると、以下のページが表示されるようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/d122f57c-9f0e-6d55-150c-92b7ff8cf96e.png)

ここで、「私の個人情報」-「高度なユーザの詳細」で、「開発モード」のチェックがONになっていると、ページ左下に「X」ボタンが出現する。
これをクリックすると、コードエディタが下部に表示され、出力を確認しながらコードを書けるようになっている。

次に、コードの実装に移るが、Visualforceの実装の前に、VisualforceページコントローラをApexで作成する必要がある。

Apexクラスの作成は、
「設定」-「プラットフォームツール」-「カスタムコード」-「Apexクラス」を選択。「新規」ボタンを押し、ここでは「ChartController」という名前でクラスを作成する。

```Apex
public class ChartController {

    static String searchText;
    
    public String getSearchText() {
        return searchText;
    }
 
    public void setSearchText(String s) {
        searchText = s;
    }
    List<Data> results;
    
    // Return a list of data points for a chart
    public List<Data> getData() {
        return ChartController.getChartData();
    }
    
    // Make the chart data available via JavaScript remoting
    @RemoteAction
    public static List<Data> getRemoteData() 
    {
        return ChartController.getChartData();
    }

    public static Decimal Average(List<Decimal> numlist)
    {
        Decimal ret=0;
        Decimal sum=0;
        for(Decimal d : numlist)
        {
            sum += d;
        }
        if(numlist.size()>0)
        {
            ret = sum/numlist.size();
        }
        return ret;
    }
    
    public List<Data> getResults() {
        return results;
    }
     
    public PageReference doSearch() {
        results = getChartData();
        return null;
    }
    
    // The actual chart data; needs to be static to be
    // called by a @RemoteAction method
    public static List<Data> getChartData() {
    
        List<Data> data = new List<Data>();
        List<List<Decimal>> currentPH = new List<List<Decimal>>();
        List<List<Decimal>> voltagePH = new List<List<Decimal>>();
        List<List<Decimal>> tempPH = new List<List<Decimal>>();
        
        for(Integer i=0;i<18;i++)
        {
            currentPH.add(new List<Decimal>());
            voltagePH.add(new List<Decimal>());
            tempPH.add(new List<Decimal>());
        }
        
        Date d = Date.newInstance(Integer.valueOf(searchText.left(4)), Integer.valueOf(searchText.mid(5, 2)), Integer.valueOf(searchText.mid(8,2)));
        String dateP = String.valueOf(d.addDays(-1));
        String dateL = String.valueOf(d.addDays(1));
        List<GeneratorLogStack__b> lst = [SELECT No__c, Date__c, Current__c, Voltage__c, Temperature__c FROM GeneratorLogStack__b WHERE No__c < :dateL AND No__c > :dateP];
        
        for (Integer i=0;i<lst.size();i++)
        {
            GeneratorLogStack__b entity=lst[i];
            Integer hour = Integer.valueOf(entity.No__c.mid(11,2));
            currentPH[hour].add(entity.Current__c);
            voltagePH[hour].add(entity.Voltage__c);
            tempPH[hour].add(entity.Temperature__c);
        }
        for(Integer i=7;i<18;i++)
        {
            data.add(new Data(i.format(), Average(currentPH[i]), Average(voltagePH[i]), Average(tempPH[i])));

        }
        return data;
    }
    
    
    // Wrapper class
    public class Data {
        public String name { get; set; }
        public Decimal data1 { get; set; }
        public Decimal data2 { get; set; }
        public Decimal data3 { get; set; }
        public Data(String name, Decimal data1, Decimal data2, Decimal data3) {
            this.name = name;
            this.data1 = data1;
            this.data2 = data2;
            this.data3 = data3;
        }
    }
}
```

【参照】
[Visualforce Charting を使用した複雑なグラフの作成](https://developer.salesforce.com/docs/atlas.ja-jp.pages.meta/pages/pages_charting_example.htm)

Apexクラスを保存したら、再びVisualforceの編集を行う。

```Apex
<apex:page controller="ChartController">
    <apex:form >
        <apex:pageBlock mode="edit" id="block">
            <apex:pageBlockSection >
                <apex:pageBlockSectionItem >
                    <apex:outputLabel for="searchText">Search Date</apex:outputLabel>
                    <apex:panelGroup >
                        <apex:inputText id="searchText" value="{!searchText}"/>
                        <apex:commandButton value="Go" action="{!doSearch}" rerender="block" status="status"/>
                    </apex:panelGroup>
                </apex:pageBlockSectionItem>
            </apex:pageBlockSection>
            <apex:actionStatus id="status" startText="requesting..."/>
            <apex:pageBlockSection title="Results" id="results" >
                <apex:chart height="400" width="700" data="{!results}">
                    <apex:legend position="right"/>
                    <apex:axis type="Numeric" position="left" fields="data1"
                        title="Currenct(A)" grid="true"/>
                    <apex:axis type="Category" position="bottom" fields="name"
                        title="Hour">
                        <apex:chartLabel rotate="315"/>
                    </apex:axis>
                    <apex:lineSeries title="{!searchText}" axis="left" xField="name" yField="data1"
                        fill="true" markerType="cross" markerSize="4" markerFill="#FF0000"/>
                </apex:chart>
                <apex:chart height="400" width="700" data="{!results}">
                    <apex:legend position="right"/>
                    <apex:axis type="Numeric" position="left" fields="data2"
                        title="Voltage(V)"/>
                    <apex:axis type="Category" position="bottom" fields="name"
                        title="Hour">
                        <apex:chartLabel rotate="315"/>
                    </apex:axis>
                    <apex:lineSeries title="{!searchText}" axis="left" xField="name" yField="data2"
                        markerType="circle" markerSize="4" markerFill="#8E35EF"/>
                </apex:chart>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
```

このコードを保存して実行すると、以下のような画面になる。
テキストボックスに年月日を「yyyy-MM-dd」形式で指定すると、該当する日付の電流／電圧グラフが表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/627239/78f7ee7b-18f3-a61f-6b77-5185d444687b.png)

あとは、このVisualforceページ用のタブを作成するだけである。

「設定」-「プラットフォームツール」-「ユーザインターフェース」-「タブ」を開き、Visualforceページタブを新規作成する。
作成したタブを、任意のアプリケーションに追加すると画面から簡単にアクセスできるようになる。

もう少しUIを工夫すれば、標準レポートを使用せず、最初からすべてのデータをBigObjectに投入し、Visualforceページで監視することも可能だ。

[【太陽光発電の発電量をロギングしてみた。目次】](https://qiita.com/takoyakiya/items/6b318ab83bcac86c966c)