時間ごとにデータを保存する方法
投稿者 daive  (社会人)
投稿日時
2017/9/6 12:36:37
なにやら、基礎が出来ていないのに、
先へ進もうとしていますが、崩壊パターンです。
’
1.モジュール化、クラス化、なぜオブジェクトモデルなのか?とか学習してください。
2.今回のモノであれば、 ざっくり、通信系、ロガー系、トレンド系、帳票系
と共通機能、などへ機能分割すべきです。
3.通信系は、通信、エラー処理、リングバッファへの書き込み(タイムスタンプ込み)
4.ロガー系は、指定周期でファイルへの書き出し、エラー処理
5.トレンド系、帳票系の前に、ファイルを扱う共通部分や、その他共通部分を考え作り、デバッグ
’
以上おせっかいです。
先へ進もうとしていますが、崩壊パターンです。
’
1.モジュール化、クラス化、なぜオブジェクトモデルなのか?とか学習してください。
2.今回のモノであれば、 ざっくり、通信系、ロガー系、トレンド系、帳票系
と共通機能、などへ機能分割すべきです。
3.通信系は、通信、エラー処理、リングバッファへの書き込み(タイムスタンプ込み)
4.ロガー系は、指定周期でファイルへの書き出し、エラー処理
5.トレンド系、帳票系の前に、ファイルを扱う共通部分や、その他共通部分を考え作り、デバッグ
’
以上おせっかいです。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2017/9/8 09:32:10
いろいろなやり方があるとは思いますが、
たとえばこんな手順的は如何でしょう。(サンプルコードは掲載しないでおきます)
(1) ローカル変数に受信した X, Y データを、フィールド変数に保持しておきます。
受信処理が複数回行われた場合に備え、コレクションで管理する必要がある場合は、
Queue(Of ) などを利用できるかと思います。
※ Invoke / BeginInvoke は使いません。
(2) Timer1_Tick が呼ばれたら、(1) で保存していたデータを取り出して
ファイルに保存します。この保存の方法としては:
<A案> ファイルには「追記」モードで記述し、書き込み終わった分は(1)から取り除く。
<B案> ファイルは毎回「新規作成」し、今まで受信した分すべてを毎回作り直す。
などのパターンがあります。
なお、データ受信のスレッドと、そのデータを利用する(この場合はファイル出力)スレッドが
異なっていますので、何らかの同期制御も必要になります。
(たとえば SyncRoot プロパティを SyncLock するなど)
たとえばこんな手順的は如何でしょう。(サンプルコードは掲載しないでおきます)
(1) ローカル変数に受信した X, Y データを、フィールド変数に保持しておきます。
受信処理が複数回行われた場合に備え、コレクションで管理する必要がある場合は、
Queue(Of ) などを利用できるかと思います。
※ Invoke / BeginInvoke は使いません。
(2) Timer1_Tick が呼ばれたら、(1) で保存していたデータを取り出して
ファイルに保存します。この保存の方法としては:
<A案> ファイルには「追記」モードで記述し、書き込み終わった分は(1)から取り除く。
<B案> ファイルは毎回「新規作成」し、今まで受信した分すべてを毎回作り直す。
などのパターンがあります。
なお、データ受信のスレッドと、そのデータを利用する(この場合はファイル出力)スレッドが
異なっていますので、何らかの同期制御も必要になります。
(たとえば SyncRoot プロパティを SyncLock するなど)
投稿者 vb素人  (学生)
投稿日時
2017/9/8 16:32:22
魔界の仮面弁士さま
ありがとうございます。
教えていただいた方法でトライしてみました。
まず、グローバル変数を用意して、
Private xdata As Double
Private ydata As Double
シリアル通信で受信したデータをグローバル変数に渡します。(☆の部分)
①
Private Delegate Sub Delegate_RcvXDataToTextBox(xdec As String)
Private Delegate Sub Delegate_RcvYDataToTextBox(ydec As String)
②
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'細かい所は省略します
Dim datax As Byte() = {bin(2), bin(1)}
Dim x As UShort = BitConverter.ToUInt16(datax, 0) '符号なし2 バイト整数に変換
Dim datay As Byte() = {bin(4), bin(3)}
Dim y As UShort = BitConverter.ToUInt16(datay, 0) '符号なし2 バイト整数に変換
Received(x, y)
End Sub
③
Private Sub Received(x As UShort, y As UShort)
If InvokeRequired Then
Invoke(New Action(Of UShort, UShort)(AddressOf Received), x, y)
Else
'グラフの設定などは省略
'☆
xdata = 1023 - x
ydata = 1023 - y
End If
End Sub
このあと、↓の④でなんとなくできました。
↓の内容ですが、TextBoxでデータを保存する数を指定しています。
その後、0.1秒おきにタイマーイベントを発生させて、残りのデータが0になったらファイルへの書き込みを終了させます。
④
Dim CountData As Integer
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
CountData = CInt(TextBox1.Text)
Timer1.Interval = 100
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
CountData = CountData - 1
timeLabel.Text = "残り" & CountData & "個"
'Saveコード
Dim sw As System.IO.StreamWriter
sw = New System.IO.StreamWriter("accel.csv", True,
System.Text.Encoding.GetEncoding(932))
sw.WriteLine(xdata & "," & ydata)
sw.Close()
If CountData = 0 Then
Timer1.Enabled = False
MessageBox.Show("測定終了")
End If
End Sub
これで時間ごとにデータを保存することはできました。
次のステップとして、
①時間もファイルに書き込む
②時間を横軸としてx,yのデータをグラフに表示させる(オシロスコープみたいなイメージ)
①についてTimer1.Tickの中で、0.1秒ずつ加算していけばできると思い、
Dim time As Double
time += 0.1
を書いてみたのですが、保存されるtimeは0.1のまま変化がありません。
初歩的な間違いだと思うのですが、教えていただけると助かります。
ありがとうございます。
教えていただいた方法でトライしてみました。
まず、グローバル変数を用意して、
Private xdata As Double
Private ydata As Double
シリアル通信で受信したデータをグローバル変数に渡します。(☆の部分)
①
Private Delegate Sub Delegate_RcvXDataToTextBox(xdec As String)
Private Delegate Sub Delegate_RcvYDataToTextBox(ydec As String)
②
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'細かい所は省略します
Dim datax As Byte() = {bin(2), bin(1)}
Dim x As UShort = BitConverter.ToUInt16(datax, 0) '符号なし2 バイト整数に変換
Dim datay As Byte() = {bin(4), bin(3)}
Dim y As UShort = BitConverter.ToUInt16(datay, 0) '符号なし2 バイト整数に変換
Received(x, y)
End Sub
③
Private Sub Received(x As UShort, y As UShort)
If InvokeRequired Then
Invoke(New Action(Of UShort, UShort)(AddressOf Received), x, y)
Else
'グラフの設定などは省略
'☆
xdata = 1023 - x
ydata = 1023 - y
End If
End Sub
このあと、↓の④でなんとなくできました。
↓の内容ですが、TextBoxでデータを保存する数を指定しています。
その後、0.1秒おきにタイマーイベントを発生させて、残りのデータが0になったらファイルへの書き込みを終了させます。
④
Dim CountData As Integer
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
CountData = CInt(TextBox1.Text)
Timer1.Interval = 100
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
CountData = CountData - 1
timeLabel.Text = "残り" & CountData & "個"
'Saveコード
Dim sw As System.IO.StreamWriter
sw = New System.IO.StreamWriter("accel.csv", True,
System.Text.Encoding.GetEncoding(932))
sw.WriteLine(xdata & "," & ydata)
sw.Close()
If CountData = 0 Then
Timer1.Enabled = False
MessageBox.Show("測定終了")
End If
End Sub
これで時間ごとにデータを保存することはできました。
次のステップとして、
①時間もファイルに書き込む
②時間を横軸としてx,yのデータをグラフに表示させる(オシロスコープみたいなイメージ)
①についてTimer1.Tickの中で、0.1秒ずつ加算していけばできると思い、
Dim time As Double
time += 0.1
を書いてみたのですが、保存されるtimeは0.1のまま変化がありません。
初歩的な間違いだと思うのですが、教えていただけると助かります。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2017/9/8 17:21:43
以前の指摘を繰り返すのは疲れたので、細かい所は目をつぶるとして:
> 保存されるtimeは0.1のまま変化がありません。
ローカル変数とフィールド変数を間違えているとか?
> 保存されるtimeは0.1のまま変化がありません。
ローカル変数とフィールド変数を間違えているとか?
投稿者 vb素人  (学生)
投稿日時
2017/9/11 10:25:14
魔界の仮面弁士さま
ありがとうございます。
変数の設定に誤りがありました。
修正したら、時間を加算することができました。
①シリアルデータから受信したx,yのデータと時間を保存する。
②xと時間、yと時間をグラフに表示する。(グラフタイプは、point)
ここまでできていますが、
②のグラフ表示の時に、「ライン」で表示させたいと思っています。
調べてみたところ、↓の設定で以前はライン表示ができていたようですが、
私の今の環境(vb2013)にはなさそうです。
↓
series.ShowLine = True
私の調べ方が足りないのかもしれませんが、ライン表示はできないのでしょうか。
ありがとうございます。
変数の設定に誤りがありました。
修正したら、時間を加算することができました。
①シリアルデータから受信したx,yのデータと時間を保存する。
②xと時間、yと時間をグラフに表示する。(グラフタイプは、point)
ここまでできていますが、
②のグラフ表示の時に、「ライン」で表示させたいと思っています。
調べてみたところ、↓の設定で以前はライン表示ができていたようですが、
私の今の環境(vb2013)にはなさそうです。
↓
series.ShowLine = True
私の調べ方が足りないのかもしれませんが、ライン表示はできないのでしょうか。
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2017/9/11 17:09:29
> 調べてみたところ、↓の設定で以前はライン表示ができていたようですが、
確認してみたいので、どこで調べたのか、情報の出所を教えてください。
書籍名とか、Webサイトの URL とか…。
> 私の今の環境(vb2013)にはなさそうです。
> series.ShowLine = True
series というのが、
Dim Series As System.Windows.Forms.DataVisualization.Charting.Series = Me.Chart1.Series(n)
あるいは
Dim Series As System.Windows.Forms.DataVisualization.Charting.SeriesCollection = Chart1.Series
の事だとしたら、そこに ShowLine というプロパティは無いはずです。
2013 はおろか、他のバージョンであっても。
VB6 時代の、ActiveX 版 MSChart コントロールでは、
Dim Series As MSChart20Lib.Series
Set Series = MSChart1.Plot.SeriesCollection(0)
Series.ShowLine = True
のようなコードを書くことがありましたが、それと勘違いしていたということはないですか?
> ②xと時間、yと時間をグラフに表示する。(グラフタイプは、point)
> ②のグラフ表示の時に、「ライン」で表示させたいと思っています。
グラフタイプというのは、ChartType プロパティに渡す SeriesChartType 列挙体のことですよね。
ラインで表示させたいのであれば、Point ではなく、Line/FastLine/StepLine あたりを
選択するべきだと思いますが、なぜ Point を選択されたのでしょうか?
http://hanatyan.sakura.ne.jp/chart/chart1.htm
確認してみたいので、どこで調べたのか、情報の出所を教えてください。
書籍名とか、Webサイトの URL とか…。
> 私の今の環境(vb2013)にはなさそうです。
> series.ShowLine = True
series というのが、
Dim Series As System.Windows.Forms.DataVisualization.Charting.Series = Me.Chart1.Series(n)
あるいは
Dim Series As System.Windows.Forms.DataVisualization.Charting.SeriesCollection = Chart1.Series
の事だとしたら、そこに ShowLine というプロパティは無いはずです。
2013 はおろか、他のバージョンであっても。
VB6 時代の、ActiveX 版 MSChart コントロールでは、
Dim Series As MSChart20Lib.Series
Set Series = MSChart1.Plot.SeriesCollection(0)
Series.ShowLine = True
のようなコードを書くことがありましたが、それと勘違いしていたということはないですか?
> ②xと時間、yと時間をグラフに表示する。(グラフタイプは、point)
> ②のグラフ表示の時に、「ライン」で表示させたいと思っています。
グラフタイプというのは、ChartType プロパティに渡す SeriesChartType 列挙体のことですよね。
ラインで表示させたいのであれば、Point ではなく、Line/FastLine/StepLine あたりを
選択するべきだと思いますが、なぜ Point を選択されたのでしょうか?
http://hanatyan.sakura.ne.jp/chart/chart1.htm
投稿者 vb素人  (学生)
投稿日時
2017/9/12 10:27:57
魔界の仮面弁士さま
私の確認については、
魔界の仮面弁士さまがおっしゃるとおり、
>VB6 時代の、ActiveX 版 MSChart コントロールでは、
> Dim Series As MSChart20Lib.Series
> Set Series = MSChart1.Plot.SeriesCollection(0)
> Series.ShowLine = True
>のようなコードを書くことがありましたが、それと勘違いしていたということはないですか?
これと勘違いしていました。
そして、Pointを選んだ理由についてですが、
Excelの散布図のようなグラフを描きたいと考えていて、Pointを選択しました。
横軸が、Timerでカウントする時間
縦軸は、シリアル通信から得られるデータを考えています。
質問が2つあります。
1つ目は、ライン表示について。
今の状況としては、
散布図のように点(マーカー)でプロットできている状況です。
散布図のように点(マーカー)と点(マーカー)を線で接続することができないかと思っています。
これがLineなどでできるということでしょうか。
2つ目は、Pointのグラフについて。
0.0を指定して、グラフ上にマーカーを表示させたときに、
X軸だけ1の位置にマーカーが表示されてしまいます。
s.Points.AddXY(0, 0)
X軸の値(例えば最大値)を変えても必ず1の所にデータがプロットされてしまいます。
私の確認については、
魔界の仮面弁士さまがおっしゃるとおり、
>VB6 時代の、ActiveX 版 MSChart コントロールでは、
> Dim Series As MSChart20Lib.Series
> Set Series = MSChart1.Plot.SeriesCollection(0)
> Series.ShowLine = True
>のようなコードを書くことがありましたが、それと勘違いしていたということはないですか?
これと勘違いしていました。
そして、Pointを選んだ理由についてですが、
Excelの散布図のようなグラフを描きたいと考えていて、Pointを選択しました。
横軸が、Timerでカウントする時間
縦軸は、シリアル通信から得られるデータを考えています。
質問が2つあります。
1つ目は、ライン表示について。
今の状況としては、
散布図のように点(マーカー)でプロットできている状況です。
散布図のように点(マーカー)と点(マーカー)を線で接続することができないかと思っています。
これがLineなどでできるということでしょうか。
2つ目は、Pointのグラフについて。
0.0を指定して、グラフ上にマーカーを表示させたときに、
X軸だけ1の位置にマーカーが表示されてしまいます。
s.Points.AddXY(0, 0)
X軸の値(例えば最大値)を変えても必ず1の所にデータがプロットされてしまいます。
投稿者 (削除されました)  ()
投稿日時
2017/9/12 14:39:04
(削除されました)
投稿者 魔界の仮面弁士  (社会人)
投稿日時
2017/9/12 14:41:48
質問内容が、タイトルの「時間ごとにデータを保存する方法」とかけ離れて来たので、
過去ログを分かりやすくするためにも、別の質問としてスレッドを立て直すことをお奨めします。
> Excelの散布図のようなグラフを描きたいと考えていて、Pointを選択しました。
> 散布図のように点(マーカー)と点(マーカー)を線で接続することができないかと思っています。
なるほど了解です。
そもそも元データからどういうグラフを描きたかったのかなどが
特に述べられていなかったので、何故 Point を選択したのか
分からなかったのですが、何となくイメージできました。
元質問では、ライン表示がどういうものをイメージしていたのか分かりませんでしたが、
Excel の「近似曲線」の意味ではなく、マーカーを単純に直線で繋ぐだけで良いのですね。
> これがLineなどでできるということでしょうか。
たとえば下記を見てください。
青紫な太線の S2 系統は、「Point グラフ+独自描画ライン」による実装、
もうひとつのカラフルな S1 系統は、「Line グラフ」による実装です。
なお、上部に置かれた ComboBox は、S1 系統の ChartType を変更するためのオマケです。
過去ログを分かりやすくするためにも、別の質問としてスレッドを立て直すことをお奨めします。
> Excelの散布図のようなグラフを描きたいと考えていて、Pointを選択しました。
> 散布図のように点(マーカー)と点(マーカー)を線で接続することができないかと思っています。
なるほど了解です。
そもそも元データからどういうグラフを描きたかったのかなどが
特に述べられていなかったので、何故 Point を選択したのか
分からなかったのですが、何となくイメージできました。
元質問では、ライン表示がどういうものをイメージしていたのか分かりませんでしたが、
Excel の「近似曲線」の意味ではなく、マーカーを単純に直線で繋ぐだけで良いのですね。
> これがLineなどでできるということでしょうか。
たとえば下記を見てください。
青紫な太線の S2 系統は、「Point グラフ+独自描画ライン」による実装、
もうひとつのカラフルな S1 系統は、「Line グラフ」による実装です。
なお、上部に置かれた ComboBox は、S1 系統の ChartType を変更するためのオマケです。
Option Strict On
Imports System.Windows.Forms.DataVisualization.Charting
Public Class Form1
Private WithEvents Chart1 As Chart
Private WithEvents ComboBox1 As ComboBox
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Chart1 = New Chart() With {.Dock = DockStyle.Fill}
Controls.Clear()
Controls.Add(Chart1)
ComboBox1 = New ComboBox() With {.Dock = DockStyle.Top}
ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
ComboBox1.DataSource = [Enum].GetValues(GetType(SeriesChartType))
Controls.Add(ComboBox1)
Chart1.Series.Clear()
Chart1.ChartAreas.Clear()
Dim ca = Chart1.ChartAreas.Add("CA")
Dim s1 = Chart1.Series.Add("S1")
Dim s2 = Chart1.Series.Add("S2")
s1.BorderWidth = 3
s1.BorderDashStyle = ChartDashStyle.Dot
s1.ChartType = SeriesChartType.Line
s2.MarkerSize = 15
s2.MarkerStyle = MarkerStyle.Circle
s2.MarkerColor = Color.BlueViolet
s2.ChartType = SeriesChartType.Point
s2.BorderDashStyle = ChartDashStyle.Solid
ComboBox1.SelectedItem = s1.ChartType
AddHandler ComboBox1.SelectionChangeCommitted, Sub() s1.ChartType = DirectCast(ComboBox1.SelectedItem, SeriesChartType)
s2.Points.AddXY(3, 5)
s2.Points.AddXY(4, 55)
s2.Points.AddXY(5, 25)
s2.Points.AddXY(1, 70)
s1.Points.AddXY(1, 10)
s1.Points.AddXY(3, 30)
s1.Points.AddXY(2, 10)
s1.Points.AddXY(2, 40)
s1.Points.AddXY(4, 80)
s1.Points.AddXY(3, 80)
s1.Points(0).Color = Color.OrangeRed
s1.Points(1).Color = Color.ForestGreen
s1.Points(2).Color = Color.DeepPink
s1.Points(3).Color = Color.DarkCyan
s1.Points(4).Color = Color.DarkGoldenrod
s1.Points(5).Color = Color.Aqua
s1.Points.ToList().ForEach(Sub(p) p.MarkerSize = 21)
s1.Points(0).MarkerStyle = MarkerStyle.Square
s1.Points(1).MarkerStyle = MarkerStyle.Cross
s1.Points(2).MarkerStyle = MarkerStyle.Diamond
s1.Points(3).MarkerStyle = MarkerStyle.Star5
s1.Points(4).MarkerStyle = MarkerStyle.Triangle
s1.Points(5).MarkerStyle = MarkerStyle.Star10
End Sub
Private Sub Chart2_PrePaint(sender As Object, e As ChartPaintEventArgs) Handles Chart1.PrePaint
Dim cg = e.ChartGraphics
Dim pre As PointF? = Nothing
Using myPen As New Pen(Brushes.BlueViolet, 4)
myPen.DashStyle = Drawing2D.DashStyle.Dash
For Each p In Chart1.Series("S2").Points
Dim x = cg.GetPositionFromAxis("CA", AxisName.X2, p.XValue)
Dim y = cg.GetPositionFromAxis("CA", AxisName.Y2, p.YValues(0))
Dim cur = cg.GetAbsolutePoint(New PointF(CSng(x), CSng(y)))
If pre IsNot Nothing Then
e.ChartGraphics.Graphics.DrawLine(myPen, pre.Value, cur)
End If
pre = cur
Next
End Using
End Sub
End Class
投稿者 (削除されました)  ()
投稿日時
2017/9/14 10:06:00
(削除されました)
投稿者 vb素人  (学生)
投稿日時
2017/9/15 08:47:57
魔界の仮面弁士さま
ありがとうございます。
「時間ごとにデータを保存する」については解決することができましたので、このスレッドは解決とさせていただきます。
別スレッドで散布図のデータをラインで結ぶについて質問させていただきたいと思います。
ありがとうございます。
「時間ごとにデータを保存する」については解決することができましたので、このスレッドは解決とさせていただきます。
別スレッドで散布図のデータをラインで結ぶについて質問させていただきたいと思います。
↓の①~③のコードで受信データxとyの保存まで対応できていますが、
自分で設定した時間(TextBox1で設定)ごとにxとyのデータを保存する方法が分かりません。
①
Private Delegate Sub Delegate_RcvXDataToTextBox(xdec As String)
Private Delegate Sub Delegate_RcvYDataToTextBox(ydec As String)
②
Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
'細かい所は省略します
Dim datax As Byte() = {bin(2), bin(1)}
Dim x As UShort = BitConverter.ToUInt16(datax, 0) '符号なし2 バイト整数に変換
Dim datay As Byte() = {bin(4), bin(3)}
Dim y As UShort = BitConverter.ToUInt16(datay, 0) '符号なし2 バイト整数に変換
Received(x, y)
End Sub
③
Private Sub Received(x As UShort, y As UShort)
If InvokeRequired Then
Invoke(New Action(Of UShort, UShort)(AddressOf Received), x, y)
Else
'グラフの設定などは省略
s.Points.AddXY(x, y)
'★
Dim sw As System.IO.StreamWriter
sw = New System.IO.StreamWriter("abc.csv", True,
System.Text.Encoding.GetEncoding(932))
sw.WriteLine(x & "," & y)
sw.Close()
End If
End Sub
Button3を押したらデータの保存を開始して、
Timer1_Tick内(☆印の箇所?)でxとyをファイルに書き込むのかと思っていますが、
xとyのデータを受け渡す方法が上手くいきません。
④
Dim CountTimer As Integer
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
CountTimer = CInt(TextBox1.Text)
Timer1.Interval = 1000
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
CountTimer = CountTimer - 1
'☆
If CountTimer = 0 Then
Timer1.Enabled = False
MessageBox.Show("測定終了")
End If
End Sub