Programming of the coffee blend.

Programming of the coffee blend.

プログラマーで焙煎士な人専用。スーパーニッチブログです。プログラミングは初心者用に解説していきます。パソコンがあれば無料のツールを使い小学生でも作れるソース付きのソフトを公開していきます。ちょっとしたコーヒーの話もあります。

Amebaでブログを始めよう!

配布しているソースにバグがありましたので入れ替えています。トップ行のコメントを参照してください。修正ソース(Form1.vb)と(database.vb)です。

前回は、メイン画面の表示部分について解説しました。今回は、各種機能ボタンの解説です。その前にリストビューやボタン等のコンテンツのNameは、コンテンツを配した際に既定名称が付きます(Button1とかListView1とか)。本来は、そのままにすべきではありません。ボタンなら先頭にbtnを付けたりリストビューならlvを付けたりと統一すべきです。たとえば登録ボタンなら「btnTouroku」とか。本ソフトは、自分のためだけのソフトなので既定のままにしています。仕事上のソースなら駄目ですね。なお、VisualBasicは親切なのでデザイン画面で名称を変えるとソースコードも自動で変えてくれます。試してみてください。

 

①登録ボタン

ListView1選択対象のデータを登録します。最終行は、新規追加、それ以外は、上書きとなります。処理は単純で①選択データを確定し②入力内容を取得③エラーチェック④登録⑤画面リセットの流れです。

    Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click 登録ボタンのイベントハンドラ 
        If ListView1.SelectedIndices.Count = 0 Then 未選択は何もしない
            Return
        End If
        idxSelect = ListView1.SelectedIndices(0) 選択データ取得
        Dim st As ST_BLEND ワーク用の器用意
        st.init() 
        If idxSelect >= stBlends Then
            '追加
        Else
            '既存
            st.copy(stBlend(idxSelect)) 変更前の内容を取得 固有idが必要
        End If
        Select Case save(st) 入力内容を取得する エラーがある場合は、出る。
            Case 1
                MsgBox("焙煎豆の選択無し。", MsgBoxStyle.OkOnly Or MsgBoxStyle.Exclamation, Me.Text)
                Return
            Case 2
                MsgBox("配合率が100%ではありません。", MsgBoxStyle.OkOnly Or MsgBoxStyle.Exclamation, Me.Text)
                Return
        End Select
        web_WriteBlendDB(st) DB更新
        '登録完了
        lblSave.Visible = True 「登録完了」のラベルを表示
        nsave = 30 タイマーイベントでカウントダウンし0でラベルを消している
        Dim top As Integer = ListView1.TopItem.Index 画面リセット後にListView1の状態を戻すためまず可視トップ行のインデックスを退避している
        'メインリスト
        resetBlendList() 再表示
        '登録したリスト 
        Dim idx As Integer = stBlends 選択すべき行を特定する
        'idの指定あるのでインデックス取得
        For i As Integer = 0 To stBlends - 1
            If stBlend(i).id = st.id Then
                idx = i
                Exit For
            End If
        Next
        '↓行変更しているので 更新確認イベント拾わないようにする
        idxBlendNow = -1
        '※[追加]あるので履歴数が0ということは無い
        ListView1.Items(idx).Selected = True 選択すべき行を選択状態にする
        ListView1.Select()
        '一旦解除
        ListView1.EnsureVisible(ListView1.Items.Count - 1) 可視トップ行を再設定
        ListView1.EnsureVisible(top)
        resetDetail() これ要らないかも
    End Sub


②削除ボタン

ListView1選択対象のデータを削除します。処理は単純で①選択データを確定し②確認問い合わせ③削除④画面リセットの流れです。ほぼ登録処理と同じです。

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        If ListView1.SelectedIndices.Count = 0 Then 選択していなれければ何もしない
            Return
        End If
        idxSelect = ListView1.SelectedIndices(0) 選択行を取得
        If idxSelect >= stBlends Then
            '追加
            Return 最下行は新規追加なので何もしない
        Else
            '既存
        End If
        Dim st As ST_BLEND  これ要らない 登録処理コピーして作ったので無駄なのが残ったままになってた
        st.copy(stBlend(idxSelect))  これ要らない
        If MsgBox("削除します。", MsgBoxStyle.YesNo, Me.Text) <> MsgBoxResult.Yes Then 問い合わせ
            Return
        End If
        '削除
        web_deleteBlendDB(idxSelect) インデックスを渡して削除している この中でメモリからも削除している
        'メインリスト
        resetBlendList() リストを再表示
        '↓行変更しているので 更新確認イベント拾わないようにする 以下で選択を最下行の追加にしている
        idxBlendNow = -1
        '※[追加]あるので履歴数が0ということは無い  
        ListView1.Items(stBlends).Selected = True
        ListView1.Select()
        ListView1.EnsureVisible(stBlends)
        resetDetail()
    End Sub

 

③複写ボタン

ListView1選択対象のデータを複製して新規登録します。コピー元をベースにして焙煎豆を変えたり配合率を変える場合に重宝します。処理は単純で①選択データを確定し②ブレンドデータをコピーし固有idをクリア③登録④画面リセットの流れです。これもほぼ登録処理と同じです。

    Private Sub Button7_Click(sender As Object, e As EventArgs) Handles Button7.Click
        If ListView1.SelectedIndices.Count = 0 Then 選択していなれければ何もしない
            Return
        End If
        idxSelect = ListView1.SelectedIndices(0) 選択行を取得
        If idxSelect >= stBlends Then
            '追加
            Return 最下行は新規追加なので何もしない
        Else
            '既存
        End If
        Dim st As ST_BLEND コピー用の器を用意
        st.copy(stBlend(idxSelect)) コピー
        If MsgBox("複写します。", MsgBoxStyle.YesNo, Me.Text) <> MsgBoxResult.Yes Then 問い合わせ
            Return
        End If
        st.id = 0 元データの固有idをクリアして新規登録としている
        Cmn_GetDate(st.ymd) ブレンド日付を今日にする
        st.taste.init() 評価もクリア
        st.todo = 0 Todoもクリア
        st.finished = 0  製品レベルフラグもクリア
        st.taste.memo = stBlend(idxSelect).id.ToString & "のコピー" & vbCrLf メモにどのブレンドデータが元になっているか分るように固有idを記録しておく

ちなみに 「vbCrLf」は、改行コードでこのままDBに格納される。
        web_WriteBlendDB(st) 登録 
        'メインリスト
        resetBlendList() リストを再表示
        '↓行変更しているので 更新確認イベント拾わないようにする
        idxBlendNow = -1
        '※[追加]あるので履歴数が0ということは無い 
        ListView1.Items(stBlends).Selected = True リスト再表示後に最下行を選択しているが追加した行にしたほうが良いかも。

登録関数で追加したデータの固有idが返ってくるのでそれを使い何番目かidxを確定して選択すればよい。(ブレンド日でソートしているため最後とは限らないため)
        ListView1.Select()
        ListView1.EnsureVisible(stBlends)
        resetDetail()
    End Sub

④「×補完」

選択したブレンドデータで×表示(欠品)になった焙煎豆を在庫豆に置き換えます。このボタンは、一度評価が完了したブレンドデータに対して使用してはいけません。複写ボタンで追加し、これから評価するブレンドに対して使用するボタンです。結構便利です。この処理の手順は、①ブレンドデータの焙煎データ一覧の中にdeleteflag=1を探す。②その焙煎データと同じ種類の豆(beanmasterID)を持ち And 焙煎度が同じ And  deleteflag=0 の焙煎データを上から順に検索しあれば入れ替える。③入れ替えたら配合率欄の表示を入れ替える。という処理を行います。ここで悩んだのは、同種の在庫を探すときに豆種(beanmaster)にしたことです。本来は、購入データ(bean)にすべきかもしれません。ただ私の場合は、数kg単位でしか購入しないため入れ代わりが激しく検索してもヒットしない確率のほうが多いので豆種が同じなら「まぁいいか」となりました。あくまでも補完なのでこれでいいでしょう。

    Private Sub Button10_Click(sender As Object, e As EventArgs) Handles Button10.Click
        '焙煎リストチェックしているidxをリストアップ
        Dim idxs() As Integer = {-1, -1, -1, -1, -1}
        Dim b As Boolean = False
        For i As Integer = 0 To ListView2.CheckedIndices.Count - 1 欠品があるかをチェック
            idxs(i) = ListView2.CheckedIndices.Item(i)
            If stCoffee(idxs(i)).deleteflag = 1 Then
                b = True
            End If
        Next
        If b = False Then
            Return 欠品がないので何もしない
        End If
        If MsgBox("欠品焙煎を在庫に置き換えます。", MsgBoxStyle.YesNo, Me.Text) <> MsgBoxResult.Yes Then 実行問い合わせ
            Return
        End If
        'この焙煎の中で欠品を入れ替える
        For i As Integer = 0 To idxs.Length - 1
            If idxs(i) < 0 Then
                Continue For
            End If
            '欠品じゃなければスキップ
            If stCoffee(idxs(i)).deleteflag = 0 Then
                Continue For
            End If
            'この焙煎と同じ豆(stCoffee(j).bean.beanmasterid)が同一 同じ焙煎baisenしかも欠品じゃない検索
            For j As Integer = 0 To stCoffees - 1 焙煎データの先頭(古い順)から全件検索
                If stCoffee(j).deleteflag = 1 Then
                    '欠品はスキップ
                    Continue For  Forの先頭へジャンプ

                End If
                '豆の種類同一か
                If stCoffee(j).bean.beanmasterid = stCoffee(idxs(i)).bean.beanmasterid Then 同じ豆種か判定 購入データが同じか判定するなら「~.bean.id」を比較すればよい
                    '焙煎度同じか
                    If stCoffee(j).baisen = stCoffee(idxs(i)).baisen Then 焙煎深度が同じか判定
                        '豆・焙煎度同じでしかも欠品なしなので入れ替え 
                        '×の%
                        Dim per As Integer = blendPar(idxs(i))
                        ListView2.Items.Item(idxs(i)).Checked = False 元の欠品していた焙煎データのチェックを外す
                        blendPar(j) = per
                        skipblendParReset = True 焙煎データをチェックした際にイベント処理で配合率を0にしているのでそれをスキップするフラグを立てている
                        ListView2.Items.Item(j).Checked = True 新たな焙煎データをチェックする
                        Exit For
                    End If
                End If
            Next
        Next
    End Sub

 

以上でメイン画面の機能ボタン系の解説を終わります。それ以外のボタンやメニューは、サブ画面(子画面)を呼び出しての処理となるため次回は、子画面のソース公開と解説の記事です。

 

コーヒー談話

初めて生豆を購入したのは、4種類。わけも分らず自作の焙煎機をただカラカラ回して10数回。焙煎日や状態、それを使ったブレンド管理をエクセルで書いていたが次の生豆を購入した辺りで縦も横も無限に広がりつつあるセルに恐怖を感じ本ソフトの開発に着手しました。おかげで?豆の特徴も把握しやすくなり焙煎度も安定してきたように感じました。表にするということは、客観視できるということです。表にするからには、焙煎深度だったり味の評価だったり具体的に分ける必要があり自然と経験値も上がります。美味しいブレンドができないと悩んでいても表を見れば「だってあんたこの焙煎深度やってないじゃん」とか「不味いのわかっててまたこれとこれ混ぜたの?」とか教えてくれます。結構コーヒーライフが楽しくなってます。このソフトのおかげか?

 

本記事は、前回に続きメイン画面の解説です。各コンテンツの機能を紹介しつつVBのコード解説を行います。

下図の左側メインとなるブレンドリストの作成と他のコンテンツとの連動を解説します。前回も配布したソース(Form1.vb)のRegion "ブレンドリスト"の部分にListView1に関するコードを書いています。

①ListView1の作成

ツールボックスからListViewを画面に配置すると何も無いただの四角い枠です。ソースでも可能ですがデザイン画面のListView1のプロパティにてある程度成形します。列Columnsはここで初期設定しているので開いて見てください。下記のresetBlendList関数でListView1を再表示しています。

    Private Sub resetBlendList()
        ListView1.ListViewItemSorter = Nothing Listview1を成形しています。Columnsはプロパティで設定していますがここでも可能です。
        ListView1.Items.Clear() 
        ListView1.View = View.Details
        With ListView1 With内はLitview1を「.」で省略できます。VBのちょっとした余計な親切心。
            With .Items   With内はLitview1.Itemsを「.」で省略できます。
                For i As Integer = 0 To stBlends - 1 1行1レコード データ全部表示しています。
                    With .Add(stBlend(i).id.ToString) まずデータの固有idをItemsにAdd 
                        With .SubItems ListViewの2列目以降は、SubItemsにAddします。
                            .Add(Cmn_GetDateString("y/M/D", stBlend(i).ymd)) 以下、Columnsの列通りにデータを設定します。
                            .Add(stBlend(i).coffees.ToString)
                            .Add((stBlend(i).taste.kaori + 1).ToString) 評価の数値は、0スタートで保管しているので見た目は+1しています。
                            .Add((stBlend(i).taste.sanmi + 1).ToString)
                            .Add((stBlend(i).taste.nigami + 1).ToString)
                            .Add((stBlend(i).taste.amami + 1).ToString)
                            .Add((stBlend(i).taste.koku + 1).ToString)
                            .Add((stBlend(i).taste.kire + 1).ToString)
                            .Add((stBlend(i).taste.kokumotsu + 1).ToString)
                            .Add((stBlend(i).taste.aji + 1).ToString)
                            If stBlend(i).todo = 1 Then
                                .Add("●")
                            Else
                                .Add(" ")
                            End If
                        End With
                    End With
                    If stBlend(i).finished = 1 Then
                        .Item(i).BackColor() = Color.Red  製品として完成しているブレンドは、行の背景を赤にしています。
                        .Item(i).ForeColor() = Color.White
                    Else
                        .Item(i).BackColor() = Color.White
                        .Item(i).ForeColor() = Color.Black
                    End If
                Next
            End With
        End With
        '追加分 最後の行は、新規追加用の行
        With ListView1
            With .Items
                With .Add("追加")
                    With .SubItems
                        .Add("-")
                    End With
                End With
            End With
        End With
    End Sub

上のWithは対象オブジェクトのメンバーを使用する際、オブジェクト名を省略するための文です。簡易表示することでスッキリ見やすいコードにさせようとしていますがこれは間違いです。オブジェクトのメンバーを全て把握しろと強要しています。逆に見づらくなりなるのは素人でも理解できます。これを使いこなすことをテクニックとはいいません。別な方法で書いてみてください。

 

ListView1をダブルクリックすると下記2行のイベント関数が張り付きます。この中にコードを書きます。

Private Sub ListView1_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ListView1.SelectedIndexChanged

End Sub

「Handles ListView1.SelectedIndexChanged」とあるように選択を変更したときに入ってきます。この中に書くべきは、変更前のブレンドにて評価欄を編集していたか否か、編集していれば登録するか否か、次に選択したブレンドのListView2焙煎リストのチェック変更と配合率の再表示と評価欄の再表示です。

        If ListView1.SelectedIndices.Count = 0 Then 念のため選択の有無をチェック
            Return
        End If

        If idxBlendNow > 0 Then 選択変更前のブレンドデータがあるか判定 あれば配合率や評価欄を収集する。
            '選択時のデータ
            Dim st As ST_BLEND
            If idxBlendNow = stBeans Then 編集前データを一時器stにコピーする
                '追加
                st.init()
            Else
                '編集
                st.copy(stBlend(idxBlendNow))
            End If
            '編集収集
            Dim st2 As ST_BLEND
            st2.copy(st) 別の一時器st2を用意 まず編集前と同じにしておく
            Select Case save(st2) 次に画面上の入力内容をst2に保存する
                Case 0 収集成功 戻り値0以外はエラーなので無視
                    '前回とチェックして違えば問い合わせ
                    If st.isMuch(st2) = False Then 構造体内に書いた同じ内容か否かの関数を通す Falseは違いあり つまり未保存の状態
                        Dim sz As String = ""
                        If st.id = 0 Then
                            sz = "入力あり。追加する?" 新規用のメッセージ
                        Else
                            sz = st.id.ToString & " 変更あり。更新する?" 
                        End If 
                        If MsgBox(sz, MsgBoxStyle.YesNo, Me.Text) = MsgBoxResult.Yes Then メッセージボックスを出して確認
                            web_WriteBlendDB(st2) YesならDBに書き込む
                            Dim top As Integer = ListView1.TopItem.Index  現在のListView1の表示状態を保管
                            Dim idx = ListView1.SelectedIndices(0)
                            '表リセット 更新したデータの内容をListView1に再表示するため一回全体を再表示している
                            resetBlendList() 本来は、更新したデータの行だけ更新すべきだが面倒だから全部書き直している。
                            '※[追加]あるので履歴数が0ということは無い
                            ListView1.Items(idx).Selected = True 選択移動前のデータ処理が完了したので本来の選択したデータに移動する。
                            ListView1.Select()
                            ListView1.EnsureVisible(ListView1.Items.Count - 1)
                            ListView1.EnsureVisible(top)
                        End If
                    End If
            End Select
        End If
        idxBlendNow = ListView1.SelectedIndices(0) 選択中のデータを覚えておく
        resetDetail() 選択したデータの配合・評価を表示する。

 

②ListView2の処理

焙煎データ一覧表示とチェックボックスオン/オフで配合率欄に追加削除する処理をしています。初期化は、ListView1と同様の処理なので解説は省きます。焙煎データに深度0シナモン、1ミディアム、2ハイ、3シティ、4フルシティ、5フレンチ、6イタリアンを持っているので各深度の色をRGB値テーブルに設定し行背景色「.Item(i).BackColor()」にセットしています。

 .Item(i).BackColor() = System.Drawing.Color.FromArgb(DEF_BAISEN_R(stCoffee(i).baisen), DEF_BAISEN_G(stCoffee(i).baisen), DEF_BAISEN_B(stCoffee(i).baisen))
ListView2は、チェックボックス付きにしています。拾うイベントは、チェックのオン/オフです。オンで配合率欄に追加、オフで削除しています。イベント関数は、デザイン画面でダブルクリックすると自動でコードに追記されますがListViewの場合は、選択変更イベントが追記されるので目的の関数とは違います。その場合は、ListView2を選択してからプロパティ上部の雷アイコンをクリックすると呼び出せるイベント一覧が出るので「ItemChecked」の欄をダブルクリックするとコードに張り付きます。手書きでもいいのですがイベント関数を覚えるのは、プログラマーの作業では無いので自動に任せるのが良い。ただこういうイベントがあるんだと一覧を見ておくのは良いことです。

別の方法としてソースコード画面上部の左赤枠でコンテンツを選択後に右赤枠でイベントを選択するとコードに関数が追記されます。

Private Sub ListView2_ItemChecked(sender As Object, e As ItemCheckedEventArgs) Handles ListView2.ItemChecked

        If skipCheckedEvent = True Then
            Return
        End If
        Dim idx As Integer = e.Item.Index チェックしたインデックス
        Dim b As Boolean = e.Item.Checked チェック ONはTrue
        'チェックして尚且つ 率の指定済みならば初期化しない
        If skipblendParReset = True And b = True Then
            skipblendParReset = False
        Else
            blendPar(idx) = 0
        End If
        resetHaigouContent() ここで配合率欄を再表示しています。

End Sub

上のDimで宣言しているidxとbは、非常に判りづらい名称です。また、使い回しをしないのであればDimで宣言する意味はありません。間違いです。最初の記事に自分で使うソフトなので殴り書きしたとあるのは、こういうことです。だから直してください。

 

③配合と率の表示処理

表示できる焙煎データは5つまで。4つは足りない、6つは要らないので5つです。配合率は、スクロールバーで入力します。プロパティで20分割しているので5%区切りで設定できます。

    Private Sub resetHaigouContent()
        Dim ptr As Integer = 0 処理した回数用に使っています。
        Dim n100 As Integer = 0 配合率の統計用器です。
        For Each itemx As ListViewItem In ListView2.CheckedItems チェックしている行数分繰り返します。
            txtmaster(ptr).ForeColor = Color.White
            txtmaster(ptr).BackColor = System.Drawing.Color.FromArgb(DEF_BAISEN_R(stCoffee(itemx.Index).baisen), DEF_BAISEN_G(stCoffee(itemx.Index).baisen), DEF_BAISEN_B(stCoffee(itemx.Index).baisen))
            txtmaster(ptr).Text = "(" & stCoffee(itemx.Index).id.ToString 以下でテキストボックスに焙煎情報を表示しています。
            If stCoffee(itemx.Index).deleteflag = 1 Then
                txtmaster(ptr).Text &= "×"  欠品していれば「×」を出しています。後日紹介する焙煎マスター画面で欠品管理しています。
            End If
            txtmaster(ptr).Text &= ") " & web_getBeanMasterName(stCoffee(itemx.Index).bean.beanmasterid) & "(" & web_getBeanMasterCountryName(stCoffee(itemx.Index).bean.beanmasterid) & ") "
            If stCoffee(itemx.Index).base = 1 Then
                txtmaster(ptr).Text &= "<ベース用> "
            End If
            If stCoffee(itemx.Index).sanmi = 1 Then
                txtmaster(ptr).Text &= "<香味用> "
            End If
            txtmaster(ptr).Text &= "穀物味(" & (stCoffee(itemx.Index).taste.kokumotsu + 1).ToString & ")" テキスト表示はここまで
            hsmaster(ptr).Value = blendPar(itemx.Index) / 5 スクロールバーに配合率セット
            lblmaster(ptr).Text = blendPar(itemx.Index).ToString & "%" 配合率を文字で表示
            n100 += blendPar(itemx.Index)
            ptr += 1
            If ptr >= txtmaster.Length Then 5つ処理したらループ終了 つまり上から順に処理をして6つ目以上のチェックは無視です。
                Exit For
            End If
        Next
        For i As Integer = ptr To txtmaster.Length - 1 もしチェックが5つ未満なら残りの配合は、空欄にする処理
            txtmaster(i).ForeColor = Color.Black
            txtmaster(i).BackColor = Color.White
            txtmaster(i).Text = ""
            hsmaster(i).Value = 0
            lblmaster(i).Text = ""
        Next
        resetGcolor(n100) 
    End Sub

スクロールバーの変更イベントを拾ってラベルの%表示を書き直しています。

    Private Sub setPar(idx As Integer) スクロールバーのインデックス
        'idx番目にチェックしている焙煎を確定する
        Dim ptr As Integer = -1
        Dim target As Integer = -1
        For Each itemx As ListViewItem In ListView2.CheckedItems ListView2のどの焙煎データが対象かを確定している
            ptr += 1
            If idx = ptr Then
                'これ
                target = itemx.Index
                Exit For
            End If
        Next
        If target >= 0 Then
            lblmaster(idx).Text = hsmaster(idx).Value * 5 & "%"  ラベルに数値表示している
            blendPar(target) = hsmaster(idx).Value * 5 
        End If
        Dim n100 As Integer = 0
        For i As Integer = 0 To hsmaster.Length - 1
            n100 += hsmaster(i).Value * 5
        Next
        resetGcolor(n100)  統計が100%なら数値を青色 以外は赤にしている
    End Sub

スクロールバーごとにイベント関数書いているがまとめても良い。その際は、sender入っているスクロールバーのnameからインデックスを確定してsetParの引数にする。
    Private Sub HScrollBar1_Scroll(sender As Object, e As ScrollEventArgs) Handles HScrollBar1.Scroll スクロールバーのイベント 2,
3,4,5と同じ関数を書いている。
        setPar(0) 
    End Sub

 

 

以上で表示関連のコードの解説を終わります。次もメイン画面ですが登録、削除など機能関連の解説です。

いよいよメイン画面の解説です。①準備②メイン画面の取り込み③画面構成④以降ソース説明の構成です。下記にてソースをpdfで配布します。画面デザインは、さすがに手打ちするのは酷なのでテキストで配布します。取り込み方法は、②で説明します。また、メイン画面のソースを打ち込んだとしてもサブ画面の準備が出来ていませんのでエラーが出ます。サブ画面は、別の記事で紹介しますが準備だけしておきます。

 

①まず2019-11-11 第4回 開発環境で準備した状態では、下図の「From1」だけがあるはずです。これに「Form2.vb」以降のサブ画面を追加します。

 

メニュー「プロジェクト」「新しい項目の追加」を選択します。

 

windowsフォームを選択して名前は既定のまま「Form2.vb」にして追加ボタンで完了です。名前は、追加するごとに「Form3.vb」「Form4.vb」という名称が自動で付きます。この名称は、自由に変更可能ですが今は、このままにしてください。そしてこれを「Form9.vb」まで繰り返し追加してください。

 

②次にメイン画面「Form1.vb」のデザインを取り込みます。

メイン画面は、さまざまなコンテンツを多数配置しているので手打ちは酷ということで特別にテキストで配信します。本来は、ボタンやテキストボックス等々をツールボックスから選択して画面に配置します。メイン画面(Form1.vb)は、下図のようにまだ何も無い状態です。ソリューションエクスプローラーの赤枠のボタンをクリックしてフォルダー表示に切り替えます。

下図赤枠の「Form1.Designer.vb」をクリックしてソースを表示します。ちなみに下図サンプルのプロジェクトがcドライブ直下になっていますがあなたがプログラマーを目指すならこれは避けるように。必ずシステムドライブ以外をプロジェクト用として使用し丸ごとコピーしてバックアップ、バージョン管理を行うこと。

配信しているデザインデータ(Form1.Designer.vb)を全てコピーして右欄を入れ替えます。

デザインを見ると下図のようになります。

 

③画面構成

ブレンドデータは、ListView1に羅列しています。ListView2は、焙煎(coffee)全データ表示しチェックボックスにて選択可能にしています。ListView1の選択変更でListView2のチェックも連動で変更しかつその下の配合率と評価も表示し直す処理をしています。他は、機能ごとのボタンやメニューを配置しています。Timerも配置していますがくだらない事に使っています。全体の情報量が多いので画面サイズは、フルHD1920*1080に合わせてます。お持ちのPCでは画面が小さい場合は、今のご時勢です、別途拡張モニタ買ってください。

 

④ソース概要(Form1.vb

プログラマー初心者へ。windowsのソフトは、イベントドリブン方式を基礎に組み立てます。イベントドリブンとは、起動終了含めボタンクリックした、テキストを入れた、バーを動かしたといったアクションの信号を拾いそれに対してプログラムを書くということです。本ソフトは、VisualBasicというマイクロソフトが提供しているツールで画面をデザインしながらbasic言語で書いています。この方法は、本来必要な多数の手続きを見えないところで補完しているため非常に容易に作成できます。容易すぎて・・・というデメリットはありますがプログラミングを学ぶ入口には良いと思います。ただ世の中Basic程度の高級言語だけで動いているわけではありませんのでそこを忘れずに。私も古い技術者なので今時これじゃないよねというコードを書いていますが大切なのは、Basicのテクニックよりプログラムはこうあるべきという「考え方・手法」です。

 

ソースは、次の大まかな構成で成り立っています。「#Region~#Region end」って感じで分けています。

 1.各コンテンツごとのイベント処理 これがほとんどです。

 2.画面表示制御

 3.onLoad時の処理

 

ソース最初のコンテンツ用配列は、インデックスでくるくるループしたいための器です。onloadで初期化しています。

    Private txtmaster() As TextBox
    Private hsmaster() As HScrollBar
    Private lblmaster() As Label

 

⑤onload処理

デザイン画面上でコンテンツの何も無いとところをダブルリックすると自動で次がソースに書かれます。ここにまず初期化したい内容を書きます。最初に画面出るまでの処理と本ソフト終了まで変更しない内容を書きます。

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

End Sub

 

コンテンツの配列初期化

古いVBは、画面に同じコンテンツを配置した際にそのまま配列化されインデックスで参照できたと記憶していますが今は、それが出来ないみたいでソース内に配列用の器を作って初期化しています。

 

        txtmaster = New System.Windows.Forms.TextBox() {TextBox1, TextBox2, TextBox3, TextBox4, TextBox5}  {}内がコンテンツName
        hsmaster = New System.Windows.Forms.HScrollBar() {HScrollBar1, HScrollBar2, HScrollBar3, HScrollBar4, HScrollBar5}
        lblmaster = New System.Windows.Forms.Label() {Label1, Label2, Label3, Label4, Label5}

        radioa = New System.Windows.Forms.RadioButton() {a1, a2, a3, a4, a5} 以下は、5段階評価用のラジオボタン
        radiob = New System.Windows.Forms.RadioButton() {b1, b2, b3, b4, b5}
        radioc = New System.Windows.Forms.RadioButton() {c1, c2, c3, c4, c5}
        radiod = New System.Windows.Forms.RadioButton() {d1, d2, d3, d4, d5}
        radioe = New System.Windows.Forms.RadioButton() {e1, e2, e3, e4, e5}
        radiof = New System.Windows.Forms.RadioButton() {f1, f2, f3, f4, f5}
        radiog = New System.Windows.Forms.RadioButton() {g1, g2, g3, g4, g5}
        radioh = New System.Windows.Forms.RadioButton() {h1, h2, h3, h4, h5}

 

⑥データの初期化

Form1_Load内で最後にreloadAll()を呼んでいます。ここでは、全データの読み込みと画面のリセット処理を呼んでいます。Optionalは、引数無くてもあってもいいよというキーワードです。ただし、省略時の値を設定しなければなりません。散々使いまくった関数をあとになって引数付けたくなった場合に便利です。reloadAllもそのパターンです。ただ「プログラムはこうあるべき」の思想とは離れています。

Private Sub reloadAll(Optional ByVal id As Integer = 0) 引数の規定値は0

        web_ReadAllCountry()  以下は各マスター・データを読み込んでします。
        web_ReadAllShop()
        web_ReadAllBean()
        web_ReadAllCoffee()
        web_readAllBlend(nProMode)

        resetBmaster()   ListView2の焙煎データ一覧を初期化表示しています。

        resetBlendList() ListView1のブレンドデータ一覧を初期化表示しています。

        Dim idx As Integer = stBlends 引数なしはListView1最後の行
        If id > 0 Then 引数ありは、固有idからインデックスを探しています。
            'idの指定あるのでインデックス取得
            For i As Integer = 0 To stBlends - 1
                If stBlend(i).id = id Then
                    idx = i あったのでこれを初期選択
                    Exit For
                End If
            Next
        End If

idxBlendNowは、編集中のブレンドデータのインデックスを入れています。何故かと言うとListView1で選択したときにそのブレンドデータの配合や評価を表示し直しているためです。もし選択前のブレンドの評価が編集途中で登録前だとしたらその内容は消えてしまいます。選択イベントの中で表示し直す前にidxBlendNowが入っていたら編集内容の保存確認を出しています。この関数に来た時点でその必要は無しと決めているのでクリア(-1)しておきます。イベントドリブンは、この辺りが気持ち悪いですね。Basicのような高級言語や最近のスクリプト系言語全体に共通することですが見えないところで勝手にいろいろ起きてしまう。これを華麗に処理することをテクニックがあると勘違いしてしまうんですよ。まぁ仕方ない。

        idxBlendNow = -1 
        ListView1.Items(idx).Selected = True  以下で選択を変更しています。
        ListView1.Select()
        ListView1.EnsureVisible(idx) データ数によっては、画面外を選択するので選択した行を画面内に表示しています。

        resetDetail() 配合率・評価を表示し直しています。ただ上の選択のイベント内ですでに実行しているかも・・要らないかもしれません。

        If nProMode = False Then 製品管理用か試験用かで画面の背景色を変えています。同じコード別なところでも書いているので外に出したほうがいい。
            Me.BackColor = SystemColors.Control
        Else
            Me.BackColor = Color.PaleVioletRed
        End If
        Timer1.Enabled = True タイマー起動しています。これはここじゃないですね。Form1_Loadか画面表示後のイベントに書くべきです。

 

今回は、ちょろっとコードを解説しました。いろいろと反省すべき部分ありましたね。

以上です。次回は、メイン画面のソース解説の続きです。

 

前回記事データアクセス部の解説で残したテーブルのデータ構成について解説します。「店舗マスターデータ(shop)」は、前回記事を参照してください。前回と本記事である程度テーブル構造を把握しておけばカスタマイズが容易になりますので我慢して読んでください。データアクセス部のソースはここです。(database.vb

 

国マスターデータ(country)と生豆マスターデータ(beanmaster)

この二つのマスターは、読み込み後に一つにまとめています。

国マスターの構造は、簡単で名称のみです。当然1国1レコード。ただし、構造体は、生豆マスターを取り込む器を用意しています。この方が扱いに便利だからです。

        Dim id As Integer 固有id
        Dim name As String   '
        Dim stBeanMaster() As ST_BEANMASTER この国の生産生豆マスター一覧をここに保管しておきます
        Dim stBeanMasters As Integer ↑の一覧数

 

生豆マスターは、豆の種類です。国「インドネシア」の「トラジャ セレベス アラビカG1」で1レコードとなります。

        Dim id As Integer 固有id
        Dim country As Integer 紐付いている国マスターの固有id
        Dim name As String   '豆の名称
        Dim memo As String   一般的な評価等のメモに使用

        Dim aroma As String 現段階で本ソフトでは未使用
        Dim taste As String 現段階で本ソフトでは未使用

 

生豆購入データ(bean)

どの店で何の豆を何kg購入したか。購入履歴です。同じ豆でも単価や豆の状態が違うためそれを把握する内容を含んでいます。製品としてのロット管理にも重要です。

        Dim id As Integer 固有id
        Dim ymd As ST_DATE     '購入日
        Dim beanmasterid As Integer 生豆マスターの固有id
        Dim country As String   ' 現段階で本ソフトでは未使用  現バージョンになる前の名残 将来的にも使用しない
        Dim name As String   ' 現段階で本ソフトでは未使用 同上
        Dim memo As String   '現段階で本ソフトでは未使用 同上
        Dim memo2 As String   '自分で感じた豆の特徴に使用
        Dim deleteflag As Integer 欠品フラグ
        Dim shop As Integer 'shopマスターの固有id
        Dim budomari As Integer 'ロス率 % 100g中ハンドピックによる損失20gなら20と入ります。

        Dim kg As Integer 購入kg
        Dim tanka As Integer 1kg単価

 

焙煎データ(coffee)

焙煎データです。1回の焙煎で1レコード積み上がります。ここで大事な情報が漏れています。このテーブルに焙煎環境を記録するフィールドを追加すべきです。たとえば、温度、湿度、焙煎温度、焙煎時間です。現在私は、自作のハンドロースターで焙煎しているためそこまでは、記録していません。だからこのテーブルに入っていません。DBに追加して構造体とIOに追記する。簡単なので是非ご自分で追加してください。

        Dim id As Integer 固有id
        Dim deleteflag As Integer 欠品フラグ
        Dim bean As ST_BEAN     '生豆購入データの固有id  これで使用した豆が特定
        Dim bymd As ST_DATE     '焙煎日
        Dim baisen As Integer   '焙煎度DEF_ENUM_BAISENの数値が入っている
        Dim taste As ST_TASTE 評価 後述
        Dim base As Integer     '1=ベースとして使用に最適 
        Dim sanmi As Integer    '1=香味用として使用に最適 

 

ブレンドデータ(blend)と製品用ブレンドデータ(blendpro)

私は、世界一美味しいブレンドを目標に日々精進しております。このテーブルは、数々の試験した分のレコードの積み上げです。試験した分がレコード件数となるのでこのテーブルが一番データ数が多くなります。ブレンドした豆の一覧は、当然、焙煎データ(coffee)から引っ張っています。ここで記録しているのは、使用した焙煎データ(coffee)の固有idとそれの配合率です。

製品用ブレンドデータ(blendpro)は、まったく同じテーブルです。固有idがロット番号となります。対象の焙煎データ(coffee)が欠品すれば同じ豆だとしても別の焙煎データ(coffee)と入れ替え本データの複製を作ることでロット番号が変わり製品管理が可能です。詳細は、今後の記事で紹介します。

        Dim id As Integer 固有id
        Dim deleteflag As Integer 削除
        Dim finished As Integer '1=製品 製品として完成しているブレンド
        Dim ymd As ST_DATE     'ブレンド日 試験日
        Dim coffees As Integer    '焙煎組み合わせ数 現在5つまて登録可能にしている
        Dim coffee() As ST_COFFEE    '組み合わせた焙煎データ(coffee)の一覧 構造体ごと入っているがDB上は、焙煎データ(coffee)の固有idだけ
        Dim coffeeper() As Integer    上の配列に沿った配合率 '0%-100 % 通常0と100はあり得ない 5%単位で登録している。
        Dim taste As ST_TASTE 評価 後述
        Dim todo As Integer     '次回ブレンドで試したいことがある場合にチェック  内容は、上の評価に記録できる。

 

最後に焙煎データ(coffee)とブレンドデータ(blend)の構造体に含まれているST_TASTE構造体です。

単純に香りや酸味等を5段階評価し数値を保管しています。またメモもあります。これらのデータは、文字化してDBに保管しています。ちなみに穀物味とありますが私が数十年コーヒーが嫌いだった理由の味覚を数値化しています。これのエピソードは、機会があれば記事にします。お好きな評価基準をここで追加してください。

実は、評価に関しては、フレーバーホイール、フレーバーツリーといった論理的な評価方法もあります。ディープな内容ですが訓練すればどなたでも理解可能です。豆の成分等からも分類可能なほど研究も進められているため今後は、もっと簡易的に視覚化されたものが出てくるかもしれません。私の理解が深ければもう少しこの辺りのコードは、変わっていたはずです。とりあえず初版というとであしからず。

        Dim kaori As Integer    '香り
        Dim sanmi As Integer    '酸味
        Dim nigami As Integer    '苦み
        Dim amami As Integer    '甘み
        Dim koku As Integer    'コク
        Dim kire As Integer    'キレ
        Dim kokumotsu As Integer    '穀物味
        Dim aji As Integer    '総合
        Dim memo As String

この評価は、文字列として保管しています。構造体の中に文字化・再配置するサブ関数(encode、decode)があるので追加した場合は、これを変更するだけです。

 

ちなみに記事中に自作のハンドロースターとありますがこれです。初号機です笑。メンテナンスは、とても楽。今では、改良重ねて半熱風式に。また、(あまり焙煎効果は無いが)遠赤外線も完備しています。部品総額1万円以下だが工数は、えらく掛かっています。未だ改良中。ハンドローストのメリットは、豆の状態が手に取るように理解できるということ。重さはもちろん香り、音など五感に伝わるので非常に良い勉強になっています。自動ロースターに変えたときに間違いなく武器となる経験です。大工で言うと釘から作る宮大工。プログラマでいうとアセンブラでチップの特性を把握しながらソフトを組むということでしょうか。一見気が遠くなる作業ですが極めるためには、実は、最も早い手法だったりします。

 

次回、メイン画面の解説です。

 

 

前回開発環境構築で準備していたデータアクセス部(database.vb)と常用部(common.vb)のソースコードを解説します。記事では、ポイントであるコードの一部を貼り付けして解説し全体は、PDFにて配信いたします。

 

なお、本ソフト、ソース、仕様等の著作権は、全て開発者である私(Teamwindのn.h)が所有しております。ソース画面等を自分のものであるがごとく流用し公開、配布・販売することは、違法行為となりますのでご注意ください。この記事を読まれた方で個人利用のみ使用許可いたします。また、本アプリは、自分用にいろいろと改修していますが一度アップしたソースのメンテナンスまでは、基本しません。あらかじめ承知してください。

 

さていよいよ解説ですがその前にもうひとつだけ。ソースコードは、大昔VB出た頃に書いたものをずーと流用したままなので考え方の古いコードが多々含まれています。今時のコードに置き換え可能ならそうしてください。

 

常用部(common.vb)の解説 
文字通り常用関数群を書いています。主に数字操作、日付操作、文字列操作です。どれも現在は、充実したメソッドを持っていますので代替可能ですがシビアなデータ制御部分では、結構融通が利く作りになっていると思います。この辺りは、追々必要なら解説します。

 

データアクセス部(database.vb)の解説 

本ソフトに関連する各種データ構造の定義とIO関数群です。テーブル・データ構成は、第3回 データ構造にて解説しています。定数定義と各データ毎に構造体の定義、パブリックデータ変数定義、IO関数、その他補助関数を持っています。

 

MySQL専用のライブラリの使用宣言

まずモジュール宣言より前に次を宣言します。

Imports MySql.Data.MySqlClient

 

列挙子、定数定義

焙煎度数は固定で数値化しておきます。Enumの列挙子は、対応した名称とRGB配列のインデックスを兼ねています。

    Public Enum DEF_ENUM_BAISEN As Integer
        light = 0
        sinamom = 1
        middium = 2
        hight = 3
        city = 4
        fullcity = 5
        french = 6
        italian = 7
    End Enum
    Public DEF_BAISEN() As String = New String() {"ライト", "シナモン", "ミディアム", "ハイ", "シティ", "フルシティ", "フレンチ", "イタリアン"}
    Public DEF_BAISEN_R() As Integer = New Integer() {196, 189, 170, 145, 109, 75, 50, 6}
    Public DEF_BAISEN_G() As Integer = New Integer() {172, 121, 109, 93, 73, 47, 32, 4}
    Public DEF_BAISEN_B() As Integer = New Integer() {156, 65, 60, 51, 40, 28, 20, 3}

 

各テーブルの詳細

 

①生豆販売店舗マスターデータ(shop)

構造体

どのテーブルの構造体も基本的には、同じ構成です。テーブルの内容に加えて初期化用のinit()関数、データ書き込み時の洗浄関数、copy関数等が含まれています。copy関数の目的は、初期化が別途必要な場合に重宝することと実行環境によってはOSがバイト単位で調整かけ可能性があるのでそれを避けるためです(大丈夫かも。Cだとよくあるので不安)。sanitize関数は、MySQLに文字列データを書き込む場合にSQL文に影響を与える文字を排除します。内容に関しては、本ソフトでは、name店舗名ぐらいしか使っていませんがメモ代わりにemail、振込み先の銀行データを持っています。

 

    Public Structure ST_SHOP
        Dim id As Integer 固有id
        Dim name As String   '
        Dim email As String   '
        Dim bank As String   '
        Dim koza As String   '
        Public Sub init() 初期化
            id = 0
            name = ""
            email = ""
            bank = ""
            koza = ""
        End Sub
        Public Sub sanitize() 洗浄
            name = cmn_SanitizeSQL(name)
            email = cmn_SanitizeSQL(email)
            bank = cmn_SanitizeSQL(bank)
            koza = cmn_SanitizeSQL(koza)
        End Sub
        Public Sub copy(st As ST_SHOP) deepコピー
            id = st.id
            name = st.name
            email = st.email
            bank = st.bank
            koza = st.koza
        End Sub
    End Structure

 

保管用の変数です。

    Public stShop() As ST_SHOP
    Public stShops As Integer

 

読み込み部です。open、close等全テーブル共通ですので外に出したほうが良いかもしれません。MySQLのライブラリ使用しています。全てのテーブルは、同様の読み書き処理ですので解説は、このテーブルだけにしておきます。

    Public Function web_ReadAllShop() As Integer
        Dim con As New MySqlConnection MySQLライブラリ用のコネクタ
        Dim cmd As MySqlCommand
        Dim drd As MySqlDataReader
        On Error GoTo _err  TRYでうまく書ける人はそうしてください。
        stShops = 0 パブリックの器に一気読みする準備
        'DBオープン
        '接続
        con.ConnectionString = "Server=localhost;user=root;password=MySQL導入時設定;database=coffee;"  localhostはMySQLを導入したPCのアドレス
        con.Open()
        If stShops = -1 Then
            Return -1 openエラーはサヨナラ
        End If
        '-------------------------------------------------------------------
        'SQL発行
        cmd = New MySqlCommand("select * from shop order by id", con) 登録順にソートしているので好きなようにしてください
        drd = cmd.ExecuteReader SQL発行
        If stShops = -1 Then
            GoTo _exit エラーはサヨナラ
        End If
        Do While (drd.Read) 終わりまでレコード一気読み
            If stShops = -1 Then
                Exit Do  エラーはサヨナラ
            End If
            'データ取得
            ReDim Preserve stShop(stShops + 1) まず器の配列を1件分拡張しておきます Preserve無いと中身がクリアされます。
            If web_readShopDR(drd, stShop(stShops)) = True Then 拡張した器に読み込んでいます。別関数にしていますが別にここでもいいです。
                stShops += 1 成功したら配列確定
            End If
        Loop
        drd.Close() テーブル後処理
        cmd.Dispose()
_exit:
        'DBクローズ
        If Not drd Is Nothing Then drd.Close() 廃棄
        If Not cmd Is Nothing Then cmd.Dispose()
        If Not con Is Nothing Then con.Close()
        Return stShops
_err:
        stShops = -1
        Resume Next
    End Function

 

con.ConnectionStringのServerは、MySQL導入したPCのアドレスを指定します。「localhost」は、自身のPCを示しています。「127.0.0.1」でも自身を指しているので同じ結果になります。例えば別のPCに導入していた場合は、そのPCのIPアドレスとなります。その際は、パスワードの強化、通信ポートの開放等、セキュリティ面での配慮が必要となります。また、レスポンスの問題も出てきます。ロジックやデータ構成の見直しは、必然。このように同一ネットワーク内で稼動しても多種の問題が出るためWANだとさらに困難を極めます。折角のDBシステムですがWEBで公開するとなると別の話(システム)になりますので機会があれば記事にします。

 

読み込み部のフィールド読み込み用補助関数です。

    Private Function web_readShopDR(ByVal drd As MySqlDataReader, ByRef stp As ST_SHOP) As Boolean 器は参照渡し
        web_readShopDR = True
        '器をクリア
        stp.init()
        On Error GoTo err_read
        stp.id = drd.GetUInt32("id") 
MySQLライブラリのメソッドを使用してフィールドを個々に読み込んでします。
        stp.name = drd.GetString("name")
        stp.email = drd.GetString("email")
        stp.bank = drd.GetString("bank")
        stp.koza = drd.GetString("koza")
        Exit Function
err_ok:
        Resume Next
err_read:
        web_readShopDR = False
        Resume Next
    End Function


 

書き込み部です。何故かMySQLライブラリ使用せずODBC使ってます。たぶん大昔のコード流用し続けているから?かMySQL以外のDBも使用していたのでその名残かも知れませんがいずれにしても初心者には、勉強になるかと思います。エラー処理が完全じゃないのでご自分で見直してください。

    Public Function web_WriteShopDB(ByRef st As ST_SHOP) As Integer
        Dim dbs As ADODB.Connection ODBCコネクタの宣言
        Dim ymd As String
        Dim szsql As String
        Dim id, i As Integer
        web_WriteShopDB = 1
        On Error GoTo err_open tryで上手に書ける方は、そうしてください。
        'DBオープン
        dbs = New ADODB.Connection()
        dbs.ConnectionString = "dsn=mysql-local;user=root;password=MySQL導入時設定;database=coffee;" ODBCに登録したdsn名を指定します。ユーザー名パスワードは、ODBCに登録済みなのでここで指定する必要はありません。DBは必要です。
        dbs.CursorLocation = ADODB.CursorLocationEnum.adUseClient
        dbs.Open() DBオープン
        Dim objRec As ADODB.Recordset 結果受信用
        szsql = web_MakeShopSQL(st) 発行するSQL文の生成 別関数に投げてますが別にここでもいいです。
        Dim btemp As Boolean = False
        If st.id = 0 Then 更新と追加両方ここに来る。追加の場合は、固有idが未確定なので追加後に取得する
            '新規
            btemp = True
        End If
        '発行
        objRec = dbs.Execute(szsql, , ADODB.CommandTypeEnum.adCmdText)
        If btemp = True Then
            '新規追加 固有id取得する
            st.id = dbs.Execute("select LAST_INSERT_ID();", , ADODB.CommandTypeEnum.adCmdText).Fields(0).Value このdbs上の最終追加レコードのidを取得している
        End If
        'メモリ入れ替え 以下器を更新している
        Dim idx As Integer = -1
        For i = 0 To stShops - 1 
            If stShop(i).id = st.id Then
                idx = i
                Exit For
            End If
        Next
        If idx = -1 Then 
            ReDim Preserve stShop(stShops + 1) 未知の固有idなので追加
            stShop(stShops).copy(st)
            stShops += 1
        Else
            stShop(idx).copy(st) すでにあるので上書き
        End If
        'DBクローズ
        objRec = Nothing DB後処理
        dbs.Close()
        dbs = Nothing
        If web_WriteShopDB = 1 Then
            '成功
            web_WriteShopDB = st.id  リターン値は固有id
        End If
        Exit Function
err_open:
        web_WriteShopDB = 0
        Resume Next
    End Function

書き込み部SQL文生成用補助関数です。固有idの有無で追加か更新かを判断しています。

    Private Function web_MakeShopSQL(ByVal st As ST_SHOP) As String
        Dim szsql As String
        '洗浄
        st.sanitize() 重要。SQL文によろしくない文字を排除しています。WEB上のシステムではこれをしないとセキュリティ突破されます。
        If st.id = 0 Then
            szsql = "INSERT INTO shop (name,email,bank,koza) VALUES (" 追加
            szsql = szsql &
                    "'" & st.name & "'," &
                    "'" & st.email & "'," &
                    "'" & st.bank & "'," &
                    "'" & st.koza & "'" &
                   ");"
        Else
            szsql = "update shop set " & 更新
                " name='" & st.name & "'," &
                " email='" & st.email & "'," &
                " bank='" & st.bank & "'," &
                " koza='" & st.koza & "'" &
                " where id = " & st.id.ToString & ";" 固有idで確定
        End If
        Return szsql
    End Function

 

以上のコードに関しては、処理をキチンと分けていますのでクラス化するのも勉強になるかもしれません。トライしてみてください。メリットデメリットを体感することはプログラマーとしては必要です。

以上がデータベース処理の大まかな解説です。他のテーブルも同様の処理なので同じ解説はしませんが次回は、各テーブルの構造の解説を行います。


 

 

本ソフトの開発環境を記事にします。MicrosftのVisualStudio2017(以下VS)を使用しVBで開発しています。プログラムは、新しいコンテンツやトリッキーなコードは、使用していないのでVSは2017より古くても大丈夫です。VSが無い場合は、無料版もあります。最新版は、VS2019「Community 2019」が無料版です。

 

VSを起動し「新しいプロジェクト・・・」を選択します。

本ソフトは、Basic言語でコーディングしているので図の選択をしています。

左欄のMyProjectを開きます。中欄の「アプリケーション」の画面で「ターゲットフレームワーク」を指定します。本ソフトは、「.NET Framework 3.5」を使用していますが4.x以降が無難です。

最大のポイントは、中欄の「参照」でデータベース用のオブジェクト(ライブラリ)を組み込むこと。1つは、ODBCアクセス用のライブラリ、もうひとつは、MySQLが配布している専用のライブラリです。下図は、すでに組み込まれた画面になっていますが追加ボタンで組み込みを開始します。

ODBC用のライブラリは、COMを選択して「Microsoft Active Data Objects 6.1」を選択してください。

MySQL専用のライブラリは、第2回 DB環境(MySQL導入)の結果インストールされたライブラリを選択します。場所は、赤欄のフォルダにある「MySQLData.dll」です。

 

この「MySQLData.dll」は、導入したMySQLのエンジンのバージョンと一致していること。ファイルのプロパティで確認できます。エンジンが8.xならMySQLData.dllも8.x、エンジンが5.xならMySQLData.dllも5.xです。

最後に中欄の「コンパイル」で「ターゲットCPU」を「x86」に設定します。

この時点では、左欄にForm1.vbしか無いはず(下図のサンプルは、完成しているのでそれ以外もある)。次のモジュールを追加してください。「common.vb」「database.vb」

左欄赤枠の右クリックメニューで「追加」「新しい項目」を選択する。

中欄の「モジュール」を選択して下部名前欄に「common.vb」を入れて追加。同様に「database.vb」を入れて追加してください。

.

 

以上で環境が整いました。この時点で「F5キー」かメニュー「デバッグ」「デバッグの開始」を選択するとソフトが生成され動き出します。ただまだ真っ白な画面が出るだけです。今後の記事で画面を作ってソースを打ち込む作業に入ります。

 

まだ本記事は、ソースに触れていませんが今後の注意点として本ソフトの著作権は、全て開発者である私(Teamwindの生豆コーヒー)が所有しております。ソース画面等を自分のものであるがごとく流用し公開、配布・販売することは、違法行為となりますのでご注意ください。

 

次回、データアクセス部のソース解説です。

本ソフトで使用するデータテーブルの解説です。本記事の目的は、前回導入したMySQLにデータベースとテーブルを作成することです。記事は、データ一覧とその内容とMySQLへの登録方法に分けています。

 

データテーブル一覧 ()はテーブル名。

①店舗マスターデータ(shop)

②国マスターデータ(country)

③生豆マスターデータ(beanmaster)

④生豆購入データ(bean)

⑤焙煎データ(coffee)

⑥ブレンドデータ(blend)

⑦製品用ブレンドデータ(blendpro)

以上をDB名「coffee」に作ります。

 

各テーブル内容(各フィールドの詳細は、今後ソースにて解説します)

①店舗マスターデータ(shop)

②国マスターデータ(country)

③生豆マスターデータ(beanmaster)

④生豆購入データ(bean)

購入日と店舗と購入量、金額等を記録します。

⑤焙煎データ(coffee)

焙煎日と深度、評価等を記録します。④と紐付いています。

⑥ブレンドデータ(blend)と⑦製品用ブレンドデータ(blendpro)

ブレンド日と使用した⑤の焙煎一覧と評価等を記録します。⑥と⑦は同じフォーマット。⑦は、製品として出した場合にロット管理として使用しています。

 

MySQLへの登録方法。

ツールを使う方法とコマンドを打つ方法がある。ツールとは、GUI上でDBの操作可能なもの。HeidiSQL(結構使える)などフリー(寄付歓迎)で利用可能なものがあります。また、前回記事で導入したMySQLのインストーラーにも「MySQL Workbench」というツールが付いています。が、使い勝手悪すぎます。有償ですが「Navicat」は、非常に便利で私も利用しています。いろいろ検討してください。ともあれ勉強重視ならコマンドを打つに限ります。以下に手順を紹介します。

MySQLを導入した際にプログラムメニューに「Command Line Client」が出来ているので起動します。

パスワードを入れます。

「coffee」DBを作成します。赤枠のcreate・・・を打ち込んでDBを作成します。show・・・で作成を確認します。

次にテーブルを作成します。

この画面からの手打ちは、辛いのでメモ帳でコマンドを打ち込んで保存、それをこの画面で取り込みます。下記は、shopテーブルのテーブル作成コマンドです。ファイル名shop.txtで保存しておきます。

このファイルを取り込みます。

最初に対象のデータベースを指定。次に保存したshop.txt(下図の例では、c:\tempに保存)をsourceコマンドで取り込んでいます。ワーニング出ているけど気にしない。最後にテーブルが出来たことを確認します。前述で紹介したツールを使っても確認することが出来ます。いろいろ試してみてください。

下記に各テーブルのcreateコマンドを記載します。

①店舗マスターデータ(shop)

CREATE TABLE `shop` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`name`  varchar(100) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`email`  varchar(100) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`bank`  varchar(100) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`koza`  varchar(30) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

②国マスターデータ(country)

CREATE TABLE `country` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`name`  varchar(50) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

③生豆マスターデータ(beanmaster)

CREATE TABLE `beanmaster` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`country`  int(11) NOT NULL ,
`name`  varchar(200) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`aroma`  varchar(200) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`taste`  varchar(200) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`memo`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

④生豆購入データ(bean)

CREATE TABLE `bean` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`ymd`  date NOT NULL ,
`beanmasterid`  int(11) NOT NULL ,
`country`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`name`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`memo`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`deleteflag`  int(11) NOT NULL ,
`shop`  int(11) NOT NULL ,
`budomari`  int(11) NOT NULL ,
`kg`  int(11) NOT NULL ,
`tanka`  int(11) NOT NULL ,
`memo2`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
PRIMARY KEY (`id`, `ymd`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

⑤焙煎データ(coffee)

CREATE TABLE `coffee` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`bymd`  date NOT NULL ,
`baisen`  int(11) NOT NULL ,
`bean`  int(11) NOT NULL ,
`taste`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`deleteflag`  int(11) NOT NULL ,
`base`  int(11) NOT NULL ,
`sanmi`  int(11) NOT NULL ,
PRIMARY KEY (`id`, `bymd`, `deleteflag`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

⑥ブレンドデータ(blend)

CREATE TABLE `blend` (
`id`  int(10) UNSIGNED NOT NULL AUTO_INCREMENT ,
`deleteflag`  int(11) NOT NULL ,
`ymd`  date NOT NULL ,
`finished`  int(11) NOT NULL ,
`coffee`  varchar(200) CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`taste`  text CHARACTER SET cp932 COLLATE cp932_japanese_ci NOT NULL ,
`todo`  int(11) NOT NULL ,
PRIMARY KEY (`id`, `deleteflag`, `ymd`)
)
ENGINE=MyISAM
DEFAULT CHARACTER SET=cp932 COLLATE=cp932_japanese_ci
AUTO_INCREMENT=1
;

⑦製品用ブレンドデータ(blendpro)

⑥のテーブル名をblendproに変えてください。あとは同じです。

 

以上です。

 

正直、DBの操作は、効率面だけ考えればツールを使うに限ります。が、勉強したいなら上記のように手打ちすることです。今後公開するソースもそうですがコピペは、勉強面からすれば逆に無駄な時間となります。今回のようなコマンドやプログラムを手打ちするということは、大工でいうと自分で道具や釘を作るということです。結果だけ必要ならコピペでもいいでしょうが自分で発展させることは、まず無理でしょうね。ソフトウェアは、機械と違って完成したからハイ終わりというものではありません(このことでいつも馬鹿なSEや営業を怒鳴りつけている)。発展できなければクズソフトです。これから勉強される方は、肝に銘じてください。それが理解出来ないなら早めにSEなり営業なりに鞍替えしたほうがいいでしょうね。

 

次回、開発環境です。

 

 

今回は、データベースMySQLの導入記事です。DBは、別に何でもいいですけど本ソフトは、MySQL使ってます。理由は、安心感と使い勝手いいから。本業でもサーバーでゴリゴリ使ってます。①入手、②導入、③その他の準備に分けてます。長いですけど簡単です。

 

①入手 

未導入は、MySQLのサイト入ってエンジンやツール入ったインストーラー入手します。ライセンス等は、自分で勉強してください。2019/11/05現在の画面です。しょっちゅう画面構成変わる感がある。

赤枠下の「MySQL Installer for Windows」でも同じです。

他に何があるんだろうと見るのも勉強なので遠回りさせてます。

現時点の最新版「mysql-installer-community-8.0.18.0.msi」を入手できました。

 

ちょっと前までは、エンジンとツールは別の場所からダウンロードしていましたが今は、このインストーラー1本に全て入ってます。

 

②導入

サーバーでもPCでも挙動は同じ。注意点は、DBエンジンが64bitだが本ソフトは、32bit対応しているのでツールは、32bitであること。これをしっかり頭に入れないと導入したのに出ない動かないと錯乱し時間を無駄にする。

 

「mysql-installer-community-8.0.18.0.msi」起動。以下の手順では、いろいろ選択肢がありますが何故そうなのかは、別途Docを読んでください。この画面では、カスタムを選択。

必要なものを左枠から選択して右に送る。選択したのは、MySQLServer(以下エンジン)とODBCとMySQLコネクタの3つ。エンジンは、DB本体。ODBCは、windowsのDBアクセス統一仕様に合わせたライブラリ。MySQLコネクタは、MySQL専用に作ったアクセスライブラリ。

この2つのコネクタは、どっちかでいいのだが本ソフトは、両方使っているので両方選択しています。理由は、過去のコードを流用していて2つとも使っているから。そもそもどっちか1つだけを使うと決めるには、2つの方法の性能を十分把握したうえでシステム仕様と全体データ件数やPC性能から確定する。「私はこっちが好き」と決めることではない。本ソフトに関しては、大量のデータは扱わない。レスポンスも重要ではないので各ライブラリの得手不得手まで考える必要は無い。2つ使ったほうが勉強にもいいでしょ?

 

完了。

環境設定が続く。

上を選択。下のinnoDBは、MySQL用に開発したデータ構造です。使うなら良し悪しあるので熟知した上で選択すること。

ServerComputerを選択。他の選択肢は、Docを見てください。

下を選択。

管理者「root」のパスワードです。weakと出ていますがローカルでこっそり使うので簡単にしています。DB設定やソースでも使うので忘れずに記録すること。

そのまま次へ。

executeで開始。

完了。

 

③その他の準備。

本ソフトは、前述のようにDBにアクセスする方法としてODBC経由とMySQLコネクタ経由両方使っている。理由は、単にコードの流用でごちゃまぜになっただけ。MySQLコネクタは、ソフトにライブラリを組み込むだけだがODBC経由は、あらかじめWindowsのODBCにアタッチする必要がある。(MySQLのODBCコネクタは、②導入時に組み込まれているので接点を設定するだけ)以下手順。

 

ODBCを起動する。場所は、C:\Windows\SysWOW64にある「odbcad32.exe」。この32というのが肝。64ビット版ではない。「システムDSN」に追加登録する。ユーザーDSNだと登録したユーザーのみに使えるのでサーバー上で部外者を除く場合に有効となる。本ソフトは、自分専用のWindowsソフトにするならユーザーDSNでもいい。お好きな方でどうぞ。

追加ボタンで選択。

プログラムからは、DataSourceName(dsn)を指定して使用します。本ソフトは、「mysql-local」としました。別に何でもいいですがソフトから使用する場合は、このdsn名で接続します。「127.0.0.1」は、PC自身のIPアドレスを示しています。何で127.・・・は、別の話ですのでググってください。

Testボタンで↓が出たらOK。出なかったらパスワードが違うかIP違う。ちなみにIPは「localhost」でもよい。

完了。

 

今回は以上です。

次回は、データ構造の説明です。

 

せっかく作ったんで汚いソースとちょっとしたテク・トラシューでも公開しようかなと。

 

ソースはVB、DBはmySQLで現状ローカルで稼動するソフト。

せっかくDB使うんでWEBまで持ってけたらその辺りのテクも書きます。

 

肝心のソフトですがコーヒー生豆から焙煎、ブレンドまで管理できるソフトです。製品版のロット管理も出来ます。

趣味が高じてどうせなら世界一美味しいオリジナルブレンドを作りたくて・・技術屋気質ですね。

 

技術解説は、VBなんで割りと初心者向けかな。

DB、WEB系に発展すれば今時のそこそこありがたい内容になるかも。

 

軽いプロフ。

私は、アセンブラ(しかないとき)からC経て業務系、最近は迎合してWEB絡みのアプリ、スクリプト系もやってます。三十ウン年、肝いりのゴリゴリオタクプログラマ。

ちなみに今回のソースは殴り書きなので汚いですが筋は通ってます。それなりのコードはこうあるべき「道」も一応もってます。なにせ聖書はカーニハン・リッチーの初版です。

 

こんな画面

メインは、試作ブレンドリストがメイン

 

サブ画面のひとつ。

生豆の購入管理から焙煎リストとその焙煎を使用したブレンドリストが出る。

 

プログラム勉強したくて尚且つ焙煎士を目指している人向けの超ニッチな記事はじめます。

 

こんなん読む人居るのかな。居なきゃただの自慰行為だなぁ。

 

次回 DB環境