ブログ

割とコンピュータよりの情報をお届けします。

コンピュータ

Windows FormsのDockプロパティFillはダブルバッファリングと相性が悪い

今回は,Windows Formsのアプリケーションを作成していた.
DataBindingを設定して,更新してみていたが どうもちらつく.原因はDockプロパティだった.
「Windows FormsのDockプロパティFillはダブルバッファリングと相性が悪い」ではなく「Windows FormsのDockプロパティFillはダブルバッファリングと相性が悪いかったかも」が正しいのかもしれない.実行環境を新しくしていくと問題が消えていく...

そもそも,本題の更新時のちらつきについて話を進める前に,
WPFのデータバインディングの場合には,バックグラウンドスレッドで更新しても普通に更新がかかるのだが,Windows Formsの場合にはUIスレッドで意図的にプロパティ変更イベントを出さないといけないようだ.
さて本題だが,Windows FormsのLabelは標準でダブルバッファリングが有効になったコントロールである.
しかし,DockプロパティをFillにしているとちらつく(ことがある).
ただし,Windows 10では問題ないように見える.例えば以下のようなコードはWindows 10で問題ない(っぽい)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private Timer timer = new Timer();
        private Label label = new Label();
        private Label label2 = new Label();
        private TableLayoutPanel tableLayoutPanel = new TableLayoutPanel();
        private Int64 count = 0;

        private ViewModel vm = new ViewModel();

        public Form1()
        {
            InitializeComponent();

            this.Controls.Add(tableLayoutPanel);
            tableLayoutPanel.Dock = DockStyle.Fill;
            tableLayoutPanel.ColumnCount = 2;
            tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            tableLayoutPanel.RowCount = 2;
            tableLayoutPanel.Controls.Add(label);
            tableLayoutPanel.SetColumn(label, 0);
            tableLayoutPanel.SetRow(label, 0);

            tableLayoutPanel.Controls.Add(label2);
            tableLayoutPanel.SetColumn(label2, 1);
            tableLayoutPanel.SetRow(label2, 1);

            this.label.DataBindings.Add(new Binding("Text", this.vm, "Text1", false));
            this.label2.DataBindings.Add(new Binding("Text", this.vm, "Text2", false));

            label.Font = new Font("MS Gothic", 40);
            label.Height = 50;
            label.Text = count.ToString();
            label.TextAlign = ContentAlignment.MiddleCenter;
            label.Dock = DockStyle.Fill;

            label2.Font = new Font("MS Gothic", 40);
            label2.Height = 50;
            label2.Text = count.ToString();
            label2.TextAlign = ContentAlignment.MiddleCenter;
            label2.Dock = DockStyle.Fill;

            timer.Interval = 100;
            timer.Tick += timer_Tick;
            timer.Start();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            count++;
            this.vm.Text1 = count.ToString();
            this.vm.Text2 = (-count).ToString();
        }
    }
}
using System;
using System.ComponentModel;

namespace WindowsFormsApp1
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string _text1 = "test";
        public string Text1
        {
            set
            {
                _text1 = value;
                RaisePropertyChanged("Text1");
            }
            get
            {
                return _text1;
            }
        }

        private string _text2 = "test";
        public string Text2
        {
            set
            {
                _text2 = value;
                RaisePropertyChanged("Text2");
            }
            get
            {
                return _text2;
            }
        }
    }
}

環境によってちらつきが発生する難しいバグ発生となった.
このバグが発生する場合には「コントロールのダブルバッファリングを有効にして、ちらつきを防止する」を実施しても関係ないようだ.
難しい.

≫ 続きを読む

2019/04/14 コンピュータ   TakeMe
タグ:Windows Forms

熱電対の温度と電圧の変換式を作る2

前回の記事では温度から熱起電力を求める式と熱起電力から温度を求める関数を用意していたが,逆が遅い.

とりあえず多項式近似なら,
前回

import numpy as np
import scipy as scipy
from scipy.optimize import fmin
import matplotlib
import matplotlib.pyplot as plt
import time

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def f(t, *args):
    return np.abs(GetK_Type_T2E(t) - args[0])

def GetK_Type_E2T(e):
    t = e / 38.921204975;
    res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='brent', tol=1e-10);
    #res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='golden', options={'xtol': 1e-15});
    print(res)
    return res.x;

if __name__ == '__main__':
    start = time.time()
    t = 20
    e = GetK_Type_T2E(t);
    print(t)
    e0 = GetK_Type_T2E(20)
    print(e0)
    t0 = GetK_Type_E2T(e0)
    print(t0)
    elapsed_time = time.time() - start
    print ("elapsed: {0}".format(elapsed_time) + " s")

今回

import numpy as np
import scipy as scipy
from scipy.optimize import fmin
import matplotlib
import matplotlib.pyplot as plt
import time

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def GetK_Type_E2T(e):
    global w;
    return np.polyval(w, e);

if __name__ == '__main__':
    start = time.time()
    ts = np.linspace(-40, 240, num=2801);
    es = GetK_Type_T2E(ts);
    
    w = np.polyfit(es, ts, 18)
    pvts = np.polyval(w, es)

    elapsed_time = time.time() - start
    print ("elapsed: {0}".format(elapsed_time) + " s")
    
今回の方が2801点計算しているのに同じ程度の時間で処理できている.

≫ 続きを読む

2019/04/11 コンピュータ   TakeMe
タグ:Python

熱電対の温度と電圧の変換式を作る

JIS C1602 熱電対 に熱起電力の多項式近似が載っているのでそれをもとに温度-起電力の変換式を用意した.

Pythonで書いてみた.
(数値の一部桁はわざと変えてある.可能性がある)細かい数値はJISを参照していただくとして,...
変換関数自体は問題ないのだが,逆関数は載っていない.
一応 数値最適化を用いて逆関数を作ってみた.
中に,numpyを入れたのはまずかったかもしれない.

import numpy as np
import scipy as scipy
from scipy.optimize import fmin

def GetK_Type_T2E(t):
    return -17.600413686 + 38.921204975 * t + 0.018558770032 * t ** 2 \
        + -0.000099457592874 * t ** 3  + 0.00000031840945719 * t ** 4 \
        + -5.6072844889E-10 * t ** 5 + 5.6075059059E-13 * t ** 6 \
        + -3.2020720003E-16 * t ** 7 + 9.7151147152E-20 * t ** 8 \
        + -1.2104721275E-23 * t ** 9 \
        + 118.5976 * np.exp(-0.0001183432 * (t - 126.9686) ** 2);

def f(t, *args):
    return np.abs(GetK_Type_T2E(t) - args[0])

def GetK_Type_E2T(e):
    t = e / 38.921204975;
    res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='brent', tol=1e-10);
    #print(res)
    return res.x;

if __name__ == '__main__':

    t = 20
    e = GetK_Type_T2E(t);
    print(t)
    e0 = GetK_Type_T2E(20)
    print(e0)
    t0 = GetK_Type_E2T(e0)
    print(t0)

このやり方だと逆関数の計算が順方向の数十倍負荷がかかってしまう. もっと時間がかかるが,以下のようにした方がずれが小さくなる(かも).

res  = scipy.optimize.minimize_scalar(f, bracket=None, bounds=(t - 0.7, t + 0.7), args=(e), method='golden', options={'xtol': 1e-15});

≫ 続きを読む

2019/04/06 コンピュータ   TakeMe
タグ:Python

C#からPythonの関数を呼び出し3

前のC#からのPythonの呼び出しの記事から新たに気が付いたことがあったので備忘録も含めて記載する.

使用するのはPython for .NETである.

git clone https://github.com/pythonnet/pythonnet.git

でgithubからpythonnetをダウンロードする.前にも述べている通りPythonの最新版は更新が速くてNugetパッケージは更新が追い付いていない.
ダウンロードされたフォルダの中にpythonnet.slnファイルがあるはずなのでこれを開き(Visual Studio 2017).
その後,ビルドの構成はReleaseWinPY3でx64(実際には実行環境に合わせる)でビルドする.するとbinフォルダにPython.Runtime.dllが出来上がる.

今回は呼び出した先のPythonのコードでmultiprocessingを使ってみた.
すると,Pythonで書かれている部分だけでなく.NET Frameworkで動いているプロセス全体が複製される.
C#から呼び出す先でマルチプロセスを使いたい場合には,プロセス全体が複製されることを前提に書いておく必要がある(複製された先からQueueなどの処理を行って処理を返さないと呼び出し元のプロセスはいつまでも待つ(特別な処理を書いていれば別だが)).

意図しない動作をしないように十分に注意する必要がある.

≫ 続きを読む

2019/03/28 コンピュータ   TakeMe
タグ:Python

Phidgets 温度センサ4入力USB熱電対インタフェースを使ってみた

Phidgets 温度センサ4入力USB熱電対インタフェースを購入してみた.
なかなか使いやすく良いが,残念な点もある.

良い点

安いところ.
そしてドライバーをインストールする必要がない点.(NIの商品より安く使い始められてドライバもいらない)
PhidgetsのWEBサイトでライブラリを配布しているが,このライブラリはシステム全体に入れる必要はなく,アプリケーションに添付するだけでも良い.(開発する場合にはNugetパッケージのインストールだけで良い)

残念な点

USBケーブルが付属しないので,別途購入する必要がある.(用意しないと使えない)
K型熱電対で分解能は0.04 度であるようなことが仕様書に書いてあるが,実際に使ってみると,確かに高い分解能の表示はしてくれるが,0.4 度程度の段差が生じる.仕様に特別に項目を設けて「Ambient Temperature Error Max ±0.5」 Kとあったのでこれのことを言っていると思われる.
(通常の表示では気付かないかもしれないが,使用したいサンプリング周波数よりオーバーサンプリングしてデジタルでローパスフィルタを掛けると量子化雑音の低減を期待できるが,確かにうまくいっている時間帯もあったが,隠しきれないステップが現れた.)
冷接点補償か何かのセンサの分解能が不足しているか量子化雑音を気にしないエンジニアがファームウェアの補償の部分を書いているのではないかと疑っている.
安いだけはある.

残念な点に対する対策(現在はこうすればうまく段差が消えるだろうという見込みの段階

0-3チャンネルに熱電対入力があるのと,4チャンネルに内蔵の温度入力ICのサンプリングがつながっている
おそらく,この1048_0B というIDのついた商品では,0-3チャンネルの値は4チャンネルの入力で補正されている.

この商品では0-3チャンネル(4チャンネル分)については電圧としてもサンプリングできるらしいので確認してみたい.ここで補償か何かのセンサの分解能が不足していることが確認できれば,自分で式を書いて滑らかになるように訂正することができる かも
温度ICの温度の値がブラックボックスになっていないのは良い.

仮想的には,測定点での起電力Aに測定器側での起電力Bの差A-Bが電圧入力に入っているはずである.
温度から熱起電力を換算する多項式近似はJISに掲載されている.
普通に使用する部分では,一対一対応なので熱起電力から温度を換算する式(関数)も作ることができる.

≫ 続きを読む

2019/03/27 コンピュータ   TakeMe

SQLiteのデータベースファイルへの同時アクセス3

先の記事の例でSQLiteで操作している部分は良いのだが,おまけのような形でつけてしまったMicrosoft Chart Controlsへのバインドに問題があることが分かった.

主に問題なのは読み込みの例で出したコードである.
this.chart1.DataBind();を最初に呼ぶのは問題ないが,毎回呼ぶと相当な負荷がかかるのだ.10000点などのプロットの場合には一瞬でもCPU負荷が100%近くに上昇してしまう.
DataBindはPointsバッファの切り替えを伴うので大きな負荷になってしまう.
Timerなどで周期的に実行する場合には,一回目の書き込みにはDataBindを使用してすでにPointがあるSeriesに対しては点の追加で対応すべき.(もちろん数珀点程度の更新なら問題ない)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SQLite;

namespace SQLView2
{
    public partial class Form1 : Form
    {
        private System.Data.SQLite.SQLiteConnection cn;
        private DataSet ds;
        private System.Data.SQLite.SQLiteDataAdapter da;

        public Form1()
        {
            InitializeComponent();

            SQLiteConnectionStringBuilder sqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = "../../../SQLite2/bin/Debug/test.db" };
            this.cn = new SQLiteConnection(sqlConnectionSb.ToString());
            this.cn.Open();

            ds = new DataSet();
            da = new SQLiteDataAdapter();
            var cmd = new SQLiteCommand(cn);
            cmd.CommandText = "SELECT * FROM test ORDER BY time asc";
            da.SelectCommand = cmd;

            da.Fill(ds, "test");
            this.dataGridView1.DataSource = ds.Tables["test"];

            chart1.Series[0].XValueMember = "time";// チャートへのバインド
            chart1.Series[0].YValueMembers = "value";
            this.chart1.DataSource = ds.Tables["test"];
            this.chart1.DataBind();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (ds.Tables["test"].Rows.Count > 0)
            {
                DateTime last = (DateTime)ds.Tables["test"].Rows[ds.Tables["test"].Rows.Count - 1][0];
                var cmd = new SQLiteCommand(cn);
                cmd.CommandText = "SELECT * FROM test WHERE time > "
                    + $"'{last.ToString("yyyy-MM-dd HH:mm:ss.fff")}'"
                    + " ORDER BY time asc";
                da.SelectCommand = cmd;
            }

            this.da.Fill(ds, "test");
            this.chart1.DataBind();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.cn.Close();
        }
    }
}

おそらく以下のように直すとよい.

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (ds.Tables["test"].Rows.Count > 0)
            {
                Int32 newDataStartingIndex = ds.Tables["test"].Rows.Count;
                DateTime last = (DateTime)ds.Tables["test"].Rows[ds.Tables["test"].Rows.Count - 1][0];
                var cmd = new SQLiteCommand(cn);
                cmd.CommandText = "SELECT * FROM test WHERE time > "
                    + $"'{last.ToString("yyyy-MM-dd HH:mm:ss.fffffff")}'"
                    + " ORDER BY time asc";
                da.SelectCommand = cmd;
                this.da.Fill(ds, "test");
                for (int i = newDataStartingIndex; i < ds.Tables["test"].Rows.Count; i++)
                {
                    for (int j = 0; j < this.chart1.Series.Count; j++)
                    {
                        this.chart1.Series[j].Points.AddXY(ds.Tables["test"].Rows[i][0], ds.Tables["test"].Rows[i][j + 1]);
                    }
                }
            }
            else
            {
                var cmd = new SQLiteCommand(cn);
                cmd.CommandText = "SELECT * FROM test ORDER BY time asc";
                da.SelectCommand = cmd;
                this.da.Fill(ds, "test");
                this.chart1.DataBind();
            }
        }

時々,書き込みのタイミングと読み込みのタイミングが連続で一致してしまうとBlockに関する例外が発生する.少しタイミングをずらして再読み込みを試みるようにした方が確実のようだ.少しバッファなどを設けて長時間SQLiteの書き込みが連続しないようにしておくとよい(複数行の書き込みはトランザクションにしないとかえってブロック時間が長くなる).

≫ 続きを読む

2019/03/27 コンピュータ   TakeMe

Mondo RescueでConoHaやさくらVPSから持ってきてVirtualBoxに移動した場合

一応Ubuntu の場合Mondo Rescueでローカルに持ってくることができる.
ただし,起動が遅い.5分間くらいかかった.(最初はフリーズしたと思っていた)何かのタイムアウトを待っている.

最も多い原因は,cloud-initである(と思う).
ConoHaなどではコントロールパネルからsshを有効にしたり無効にしたりということを実現するため,ディストリビューションの標準とこの辺に差異がある(ようだ).
一方,ローカルのVirtaulBoxにcloud-init自体が不要になるので,/etc/cloud/cloud-init.disabledを追加すると一時的に無効にできる.
中身は何もいらない.フォルダにこのファイルがあるだけでよい.

また,/etc/network/interfacesも注意だ.
あなたの環境でIPv6が使用できない場合には設定を変更しておかないとこれもまたタイムアウトを待って5分程度待ってしまう.

IPv6が使える環境かどうか,cloud-initが待っていないかどうかをsystemd-analyze blameコマンドで確認されたい.

≫ 続きを読む

2019/03/19 コンピュータ   TakeMe
タグ:新製品

Mondo Rescueで持ってきたISOイメージ使ったVirtualBoxで確認

ひとつ前の記事ではUbuntuでMondo Rescueを使う場合について言っていた.
とりあえずソースやdebパッケージを使用してインストールが終わったら
バックアップを取って確認(いざというときに使えるかを確認)

参考としては以下の2つがよさそう

Mondo Rescue を使って バックアップしてVMへリストア(復元)をやってみた

ポイントとしては,リストアはVPSから持ってきている場合には環境が変わるので/dev/vdaを/dev/sdaに読み替えるという点と,
途中の操作でmkinitrdを実行するようにダイアログがでる.
しかし,Ubuntuでそれはできないので,YESを押して(ここで,chroot /が実行されるのでcd /bootで移動して)
update-initramfs -u
または
mkinitramfs -o /boot/initrd.img-4.4.0-120-genericなどを直接実行
すること.
ただし,Ubuntu 16.04くらいのバージョンでは実行環境がよほどの違いがない限りコマンドを打つ必要はなくそのままexitでもよい.

それから,VirtualBoxでやってみたら,
一応grubをインストールしましたなどと表示は出ているのだけれど
grub-installには失敗していることがある(指示通りなら環境によるが割とうまくいくらしい).その場合には,debianのインストールisoを使って,Rescue Modeでgrub再インストールを選んでもよい.

最新のMondo Rescueではパーティション作成までリストアの途中に行うと,パーティションのUUIDまで持っていってくれるので,fstabやgrub.cfgなどは直接編集することが不要になる.(このあたりは私は間違っていた.Ubuntu 10.04くらいのころの常識で使っていたが,今はダイアログに応えていったほうが簡単である)

(Linuxのバックアップ自体は,/bootなどは除いて/をrsyncでそのままコピーしてもよいがisoファイルが作れるところが良いところかな.
別にシステムが止まっていなくてもよい(ログファイルやデータベースなどで不整合が生じる場合があるので止めた方がよいのはもちろんだが)
べつにdebianのRescue Modeを併用してしまうなら,tarでも...という気もするが,
Modo Rescueを知っている人はわかっていると思われるが,
tarでバックアップを取る・DVDなどのサイズに応じて分割・リストアという操作にユーザーインターフェースを与えてくれているソフトである)

≫ 続きを読む

2019/03/17 コンピュータ   TakeMe

Ubuntu 16.04-17.04世代のMondo Rescueのインストール

Ubuntu 16.04-17.04世代ではすでに,
Mondo Rescueを使う場合にはdebパッケージではなく,使える状態にするのは自分の責任で行うことが推奨されるレベルになってしまったようである.
できないようであれば,商用のバックアップソフトのインストールが強く推奨される.
ただし,商用のソフトなどいくらするのか...

一応Mondo RescueのパッケージはUbuntuのレポジトリにあるらしいが,すでに10.04くらいの時にすでに依存関係が壊れていて実質使えない.
すでに,Ubuntu用はユーザーが必要に応じてソースから用意すべきソフトに変わっている.

ただし,少し前までは公式のhttp://www.mondorescue.org/に各ディストリビューション用にレポジトリが公開されていて,sources.listをインポートして,GPGキーもインポートすれば使えていた.
最近,キーの形式が変更になったためと思われるが,キーのインポートを行っても,
W: GPG エラー: ftp://ftp.mondorescue.org 8 Release: 公開鍵を利用できないため、以下の署名は検証できませんでした: NO_PUBKEY 6BA8C2D220EBFB0E
とでるようになってしまった.
Mondo Rescueの主開発者は不足しており,あらゆるディストリビューションに対して最適なパッケージ提供を行えない状況が発生しているようである.18.04向けなどは提供されていない(提供されるかどうかも開発者の負荷次第とみられるがCentOS/Fedora向けrpmは優先的に取り組んでいるとみられる).

こうなると,apt-getのインストール機能は大抵いうことを聞いてくれなくなる.
一応どうしてもMondo Rescueを使用したい方ように参考ページを紹介 しておく(mondo resucue ubuntu 16.04 (mint linux18)install
たぶん,タイプミスと思われる点があったので,少し直す.(16.04は適宜読み替えて)

# wget http://www.mondorescue.org/ftp/ubuntu/16.04/mondorescue.sources.list
# sudo mv ./mondorescue.sources.list /etc/apt/sources.list.d/
# sudo apt-get update
# sudo apt-get install mondo

(2行目は参考ページではcatを使っているのだが本当はファイルに書き出す「>」などがいるようなきがする)
ただし,どうもこれだけでインストールできない.
afio, gzip, mkisofs, genisoimage, xorriso, isolinuxもインストールしておいてほしい.
適宜 sudo apt-get -f installを使用することを合わせて実施願いたい.
さらにこれでもインストールはうまくいかない.
最後にどうしてもインストールに問題が残った場合には,
ftp://ftp.mondorescue.org/ubuntu/**.**/
からmindi-busyboxやmindiやmondoを手動でダウンロードして
sudo dpkg -iで個別にインストールされたい.

すべてインストールしたら,/etc/apt/sources.list.d/mondorescue.sources.listはこのディレクトリから取り除いておくことをお勧めする.
mondoarchiveを実行できない場合にはインストールに失敗しているか,何かがふそくしていると思うのでsudo dpkg -iなどで不足分をインストールしていってほしい.

実際に使ってみるとわかるが,さらに参考「VPSをMONDO RESCUEでバックアップ」の最後の方の/etc/mindi/mindi.confの編集で
「EXTRA_SPACEとBOOT_SIZEを大きめにする」も実施が必要.

≫ 続きを読む

2019/03/16 コンピュータ   TakeMe

SQLiteのデータベースファイルへの同時アクセス2

先の記事の例がSQLiteが直接 DATETIMEを記録する型を持たずTEXTやNUMERIC(数値型でパースを試みてダメならTEXTで記録する型)に直すということについて考察が抜けている(あえてINTEGERに直していた)

どうも以下のようにしたらうまく保存できるようです.
SELECTの時にdatetime()に戻す方法もあるが,DateTimeの桁がおちてしまう(秒未満).

さらに細かくいうと,yyyy-MM-dd HH:mm:ss.fffは本当はfffではDateTimeの桁を落としてしまうのでfffffffらしい(.NETのDateTimeは100 ns単位).(JavaScriptなら1 ms単位)

書き込み

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SQLite;

namespace SQLite2
{
    public partial class Form1 : Form
    {
        private System.Data.SQLite.SQLiteConnection cn;

        public Form1()
        {
            InitializeComponent();

            SQLiteConnectionStringBuilder sqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = "test.db" };
            this.cn = new SQLiteConnection(sqlConnectionSb.ToString());
            this.cn.Open();

            var cmd = new SQLiteCommand(cn);
            cmd.CommandText = "CREATE TABLE IF NOT EXISTS test(" +
                    "time DATETIME NOT NULL PRIMARY KEY," +
                    "value REAL)";
            cmd.ExecuteNonQuery();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            DateTime datetime_ = DateTime.Now;
            Double value = datetime_.Ticks;
            string datetime = datetime_.ToString("yyyy-MM-dd HH:mm:ss.fff");
            var cmd = new SQLiteCommand(cn);
            cmd.CommandText = "INSERT INTO test(time, value) "
                               + "VALUES("
                               + $"'{datetime}', {value})";
            cmd.ExecuteNonQuery();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            cn.Close();
        }
    }
}

読み込み

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.SQLite;

namespace SQLView2
{
    public partial class Form1 : Form
    {
        private System.Data.SQLite.SQLiteConnection cn;
        private DataSet ds;
        private System.Data.SQLite.SQLiteDataAdapter da;

        public Form1()
        {
            InitializeComponent();

            SQLiteConnectionStringBuilder sqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = "../../../SQLite2/bin/Debug/test.db" };
            this.cn = new SQLiteConnection(sqlConnectionSb.ToString());
            this.cn.Open();

            ds = new DataSet();
            da = new SQLiteDataAdapter();
            var cmd = new SQLiteCommand(cn);
            cmd.CommandText = "SELECT * FROM test ORDER BY time asc";
            da.SelectCommand = cmd;

            da.Fill(ds, "test");
            this.dataGridView1.DataSource = ds.Tables["test"];

            chart1.Series[0].XValueMember = "time";// チャートへのバインド
            chart1.Series[0].YValueMembers = "value";
            this.chart1.DataSource = ds.Tables["test"];
            this.chart1.DataBind();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (ds.Tables["test"].Rows.Count > 0)
            {
                DateTime last = (DateTime)ds.Tables["test"].Rows[ds.Tables["test"].Rows.Count - 1][0];
                var cmd = new SQLiteCommand(cn);
                cmd.CommandText = "SELECT * FROM test WHERE time > "
                    + $"'{last.ToString("yyyy-MM-dd HH:mm:ss.fff")}'"
                    + " ORDER BY time asc";
                da.SelectCommand = cmd;
            }

            this.da.Fill(ds, "test");
            this.chart1.DataBind();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.cn.Close();
        }
    }
}

≫ 続きを読む

2019/03/15 コンピュータ   TakeMe