実践・連作障害回避スケジューリングツールを作る④

2021-06-19

GAS ツール プログラミング

B!

限られた農地を上手に活用する方法をプログラム側から考える。

ここまでの記事 プログラムの続きを書いてとりあえずの完成を目指します!




今回のソースファイル全文

今回の追加分です。

それぞれ、前回作成したコードファイルに追加していきます。
「~以下に追加」となっていますが、基本どこでもOKです。

define.gsに追加
// -------------------------------------------------------------------------------------------------------
// tmpシート.
// -------------------------------------------------------------------------------------------------------
var TmpDataStartX             = 3;        // 開始 X.
var TmpDataStartY             = 3;        // 開始 Y.
var TmpYearX                  = 2;        // 年度 X.
main.gsに追加
// -------------------------------------------------------------------------------------------------------
// エントリーポイント以下に追加.
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
// 「作物決定」ボタンを押した.
// -------------------------------------------------------------------------------------------------------
function PushEntry() {
  
  // シートの取得.
  var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );
  var defineSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameDefine );
  
  if ( mngSheet === null ) { return; }
  if ( dataSheet === null ) { return; }
  if ( defineSheet === null ) { return; }
  
  // 作物名が存在するかどうかチェック.
  var dataID = SearchDataSheet( dataSheet );
  if ( dataID === -1 ) { return; }
  
  var dataIdx = GetLastRow( mngSheet, MngListIDX ) + 1;
  var num, tmp;
  
  // ID.
  mngSheet.getRange( dataIdx, MngListIDX ).setValue( dataID );
  
  // 作物名.
  mngSheet.getRange( dataIdx, MngListNameX ).setValue(
    dataSheet.getRange( dataID, DataNameX ).getValue() );
  
  // 科目.
  mngSheet.getRange( dataIdx, MngListSubjectX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineVarietySubjectX ) );

  // 作業開始.
  num = dataSheet.getRange( dataID, DataSowingSeedStartX ).getValue();
  tmp = dataSheet.getRange( dataID, DataSowingSeedResultX ).getValue();
  if ( num === 0 ) {
    // 種まき作業がないので定植で考える.
    num = dataSheet.getRange( dataID, DataPlantingStartX ).getValue();
    tmp = dataSheet.getRange( dataID, DataPlantingResultX ).getValue();   
    mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( false );
  } else {
    mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( true );
  }
  mngSheet.getRange( dataIdx, MngListWorkStartX ).setValue( num + '月' );

  // 収穫.
  mngSheet.getRange( dataIdx, MngListWorkEndX ).setValue(
    dataSheet.getRange( dataID, DataHarvestStartX ).getValue() + '月' );

  // 作業終了週.
  mngSheet.getRange( dataIdx, MngListWorkEndWeekX ).setValue(
    dataSheet.getRange( dataID, DataCultivationPeriodEndX ).getValue() );

  // 輪作年限BASE.
  mngSheet.getRange( dataIdx, MngListCropRotX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  // 輪作年限残り.
  mngSheet.getRange( dataIdx, MngListCropRotRestX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  
  // カラ期間.
  if ( dataIdx !== MngListStartY ) {
    // 1個前の作業終了週をget.
    num = mngSheet.getRange( (dataIdx - 1), MngListWorkEndWeekX ).getValue();
    // 選択した作物の作業開始週と比較.
    if ( num < tmp ) {
      // カラ期間計算.
      num = tmp - num;
    } else {
      num = OneYearToWeeks + (tmp - num);
    }

    // カラ期間.
    mngSheet.getRange( dataIdx, MngListIntervalX ).setValue( num );
    
    // 前の輪作年限を全部更新.
    num = num + dataSheet.getRange( dataID, DataCultivationPeriodResultX ).getValue(); // カラ期間+栽培期間(ざっくり).
    for ( var y = MngListStartY; y < dataIdx; ++y ) {
      tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
      if ( tmp  === 0 ) { continue; }
      tmp = tmp - num;
      if ( tmp < 0 ) { tmp = 0; }
      mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
    }
  }
  
  return;
}

// -------------------------------------------------------------------------------------------------------
// 「スケジュール作成」ボタンを押した.
// -------------------------------------------------------------------------------------------------------
function PushCreateSchedule() {
  
  // シートの取得.
  var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );
  var defineSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameDefine );
  
  if ( mngSheet === null ) { return; }
  if ( dataSheet === null ) { return; }
  if ( defineSheet === null ) { return; }
  
  // 指定シート名が存在するか.
  var newSheetName = mngSheet.getRange( MngConfigSheetName ).getValue();
  var newSheet = GetSheet( newSheetName );
  
  if ( newSheet !== null ) {
    // シートを削除する.
    DeleteSheet( newSheet );
  }
  
  // 新たに作成.
  newSheet = CopySheet( SheetNameTmp, newSheetName );
  
  // 作物リストを頭から解釈してく.
  var dataEnd = GetLastRow( mngSheet, MngListIDX ) + 1;
  var year = 0, ID, isSeed, start, end, index = 1, col, name;
  
  // ~年目入力.
  DrawYear( newSheet, (year + 1) );
  
  for ( var y = MngListStartY; y < dataEnd; ++y ) {
    
    // ID.
    ID = mngSheet.getRange( y, MngListIDX ).getValue();
    // 種まきか否か.
    isSeed = mngSheet.getRange( y, MngListIsSowingSeedX ).getValue();  
    // 作業終了週.
    end = dataSheet.getRange( ID, DataCultivationPeriodEndX ).getValue(); 
    // 色.
    col = GetBGColorToSubject( defineSheet, mngSheet.getRange( y, MngListSubjectX ).getValue() );
    // 名前.
    name = mngSheet.getRange( y, MngListNameX ).getValue(); 
    
    if ( isSeed === true ) {
      // 種まき開始週.
      start = dataSheet.getRange( ID, DataSowingSeedResultX ).getValue();
    } else {
      // 定植開始週.
      start = dataSheet.getRange( ID, DataPlantingResultX ).getValue();
    }
    
    // 色塗り開始.
    if ( y !== MngListStartY ) {
      if ( index >= start ) {
        // 1年ずらす.
        ++year;
        // ~年目入力.
        DrawYear( newSheet, (year + 1) );
      }
    }

    index = start;

    // 名前入力.
    newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setValue( name );
    
    while ( true ) {
      
      // 終了チェック.
      if ( index === end ) { break; }
      
      // 色塗り.
      newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setBackground( col );
            
      ++index;
      if ( index > OneYearToWeeks ) {
        // 年数を増やす.
        ++year;
        // ~年目入力.
        DrawYear( newSheet, (year + 1) );
        index = 1;
      }
    }
  }
  
  return;
}

// -------------------------------------------------------------------------------------------------------
// 「1つ消す」ボタンを押した.
// -------------------------------------------------------------------------------------------------------
function PushOnePrev() {
  
  // シートの取得.
  var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );
  var dataSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameData );

  if ( mngSheet === null ) { return; }
  if ( dataSheet === null ) { return; }
  
  var endY = GetLastRow( mngSheet, MngListIDX );
  
  if ( endY < MngListStartY ) { return; }
  
  // 消してしまうカラ期間 + 栽培期間.
  var baseNum, tmp;
  var num =
      mngSheet.getRange( endY, MngListIntervalX ).getValue() +
      dataSheet.getRange( mngSheet.getRange( endY, MngListIDX ).getValue(), DataCultivationPeriodResultX ).getValue();
  
  RangeClear( mngSheet, MngListIDX, endY, 10, 1 );
  
  // 輪作年限を再設定.
  for ( var y = MngListStartY; y < endY; ++y ) {
    baseNum = mngSheet.getRange( y, MngListCropRotX ).getValue();
    
    if ( baseNum === 0 ) { continue; }// 輪作年限がもともとない作物はスキップ.
    
    tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
    tmp = tmp + num;
    
    // base以上にならないようにする.
    if ( tmp > baseNum ) {
      tmp = baseNum;
    }
    
    mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  }
  
  return;
}

// -------------------------------------------------------------------------------------------------------
// 「全部消す」ボタンを押した.
// -------------------------------------------------------------------------------------------------------
function PushAllDelete() {
  
  // シートの取得.
  var mngSheet = SpreadsheetApp.openById( SheetID ).getSheetByName( SheetNameMng );

  if ( mngSheet === null ) { return; }
  
  RangeClear( mngSheet, MngListIDX, MngListStartY, 10, 0 );
  
  return;
}

// -------------------------------------------------------------------------------------------------------
// private以下に追加.
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
// 現在のスプレッドシートの中の指定の名前のシートを取得.
// -------------------------------------------------------------------------------------------------------
function GetSheet( sheetName ) {
  return SpreadsheetApp.getActive().getSheetByName( sheetName );
}
// -------------------------------------------------------------------------------------------------------
// スプレッドシートを削除.
// -------------------------------------------------------------------------------------------------------
function DeleteSheet( sheet ) {
  SpreadsheetApp.getActive().deleteSheet( sheet );
  return;
}
// -------------------------------------------------------------------------------------------------------
// スプレッドシートをコピー作成&リネーム.
// -------------------------------------------------------------------------------------------------------
function CopySheet( copySheetName, newSheetName ) {
  var copy = GetSheet( copySheetName );
  if ( copy === null ) { return null; }// 失敗.
  
  var newSheet = copy.copyTo( SpreadsheetApp.getActive() );
  
  if ( newSheet === null ) { return null; }//失敗.
  
  // リネーム.
  newSheet.setName( newSheetName );
  
  return newSheet;
}
// -------------------------------------------------------------------------------------------------------
// ~年目入力.
// -------------------------------------------------------------------------------------------------------
function DrawYear( tmpSheet, year ) {
  tmpSheet.getRange( ((TmpDataStartY - 1) + year), TmpYearX ).setValue( year + '年目' );
  return;
}
// -------------------------------------------------------------------------------------------------------
// 科目名の背景カラーを取得.
// -------------------------------------------------------------------------------------------------------
function GetBGColorToSubject( defineSheet, subjectName ) {
  
  var endIdx = GetLastRow( defineSheet, DefineSubjectX ) + 1;

  var ranges = defineSheet.getRange( DefineDataStartY, DefineSubjectX, endIdx, 1 ).createTextFinder( subjectName ).findAll();

  if ( ranges.length === 0 ) { return '#ffffff'; }
  
  return ranges[0].getBackground();
}

【解説】「作物決定」ボタンの処理実装

18~62行目

  // 作物名が存在するかどうかチェック.
  var dataID = SearchDataSheet( dataSheet );
  if ( dataID === -1 ) { return; }
  
  var dataIdx = GetLastRow( mngSheet, MngListIDX ) + 1;
  var num, tmp;
  
  // ID.
  mngSheet.getRange( dataIdx, MngListIDX ).setValue( dataID );
  
  // 作物名.
  mngSheet.getRange( dataIdx, MngListNameX ).setValue(
    dataSheet.getRange( dataID, DataNameX ).getValue() );
  
  // 科目.
  mngSheet.getRange( dataIdx, MngListSubjectX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineVarietySubjectX ) );

  // 作業開始.
  num = dataSheet.getRange( dataID, DataSowingSeedStartX ).getValue();
  tmp = dataSheet.getRange( dataID, DataSowingSeedResultX ).getValue();
  if ( num === 0 ) {
    // 種まき作業がないので定植で考える.
    num = dataSheet.getRange( dataID, DataPlantingStartX ).getValue();
    tmp = dataSheet.getRange( dataID, DataPlantingResultX ).getValue();   
    mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( false );
  } else {
    mngSheet.getRange( dataIdx, MngListIsSowingSeedX ).setValue( true );
  }
  mngSheet.getRange( dataIdx, MngListWorkStartX ).setValue( num + '月' );

  // 収穫.
  mngSheet.getRange( dataIdx, MngListWorkEndX ).setValue(
    dataSheet.getRange( dataID, DataHarvestStartX ).getValue() + '月' );

  // 作業終了週.
  mngSheet.getRange( dataIdx, MngListWorkEndWeekX ).setValue(
    dataSheet.getRange( dataID, DataCultivationPeriodEndX ).getValue() );

  // 輪作年限BASE.
  mngSheet.getRange( dataIdx, MngListCropRotX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
  // 輪作年限残り.
  mngSheet.getRange( dataIdx, MngListCropRotRestX ).setValue(
    GetDataToSubject( dataSheet, defineSheet, dataID, DefineCropRotResultX ) );
選択中のセルの作物名から「data」シートを検索し、縦列をIDとして利用しています。
(別途IDをつけたい場合はdataシートを改造)

その後IDを使用し必要なデータを取り出しています。

64~77行目

  // カラ期間.
  if ( dataIdx !== MngListStartY ) {
    // 1個前の作業終了週をget.
    num = mngSheet.getRange( (dataIdx - 1), MngListWorkEndWeekX ).getValue();
    // 選択した作物の作業開始週と比較.
    if ( num < tmp ) {
      // カラ期間計算.
      num = tmp - num;
    } else {
      num = OneYearToWeeks + (tmp - num);
    }

    // カラ期間.
    mngSheet.getRange( dataIdx, MngListIntervalX ).setValue( num );
このツールでは「カラ期間」を計算しています。

…勝手に命名してますので好きに呼んでやってください。

ここで言うカラ期間は選択した作物間の空きの期間(週数)です。
スケジュールの作成がしやすいように保持しています。

79~87行目

    // 前の輪作年限を全部更新.
    num = num + dataSheet.getRange( dataID, DataCultivationPeriodResultX ).getValue(); // カラ期間+栽培期間(ざっくり).
    for ( var y = MngListStartY; y < dataIdx; ++y ) {
      tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
      if ( tmp  === 0 ) { continue; }
      tmp = tmp - num;
      if ( tmp < 0 ) { tmp = 0; }
      mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
    }

カラ期間の計算ののち、現在選択中の作物の輪作年限数の消化をしています。
コメントにも書かれていますが、(ざっくり)してます。

【解説】「スケジュール作成」ボタンの処理実装

126~179行目

  for ( var y = MngListStartY; y < dataEnd; ++y ) {
    
    // ID.
    ID = mngSheet.getRange( y, MngListIDX ).getValue();
    // 種まきか否か.
    isSeed = mngSheet.getRange( y, MngListIsSowingSeedX ).getValue();  
    // 作業終了週.
    end = dataSheet.getRange( ID, DataCultivationPeriodEndX ).getValue(); 
    // 色.
    col = GetBGColorToSubject( defineSheet, mngSheet.getRange( y, MngListSubjectX ).getValue() );
    // 名前.
    name = mngSheet.getRange( y, MngListNameX ).getValue(); 
    
    if ( isSeed === true ) {
      // 種まき開始週.
      start = dataSheet.getRange( ID, DataSowingSeedResultX ).getValue();
    } else {
      // 定植開始週.
      start = dataSheet.getRange( ID, DataPlantingResultX ).getValue();
    }
    
    // 色塗り開始.
    if ( y !== MngListStartY ) {
      if ( index >= start ) {
        // 1年ずらす.
        ++year;
        // ~年目入力.
        DrawYear( newSheet, (year + 1) );
      }
    }

    index = start;

    // 名前入力.
    newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setValue( name );
    
    while ( true ) {
      
      // 終了チェック.
      if ( index === end ) { break; }
      
      // 色塗り.
      newSheet.getRange( (TmpDataStartY + year), (index + TmpDataStartX - 1) ).setBackground( col );
            
      ++index;
      if ( index > OneYearToWeeks ) {
        // 年数を増やす.
        ++year;
        // ~年目入力.
        DrawYear( newSheet, (year + 1) );
        index = 1;
      }
    }
  }
このツールのキモと言っても過言ではない部分です。

作業期間部分のみを色塗りしていきます。色は「define」シートのセルの背景色から取得しています。

【解説】「1つ消す」「全部消す」ボタンの処理実装

200~223行目

  // 消してしまうカラ期間 + 栽培期間.
  var baseNum, tmp;
  var num =
      mngSheet.getRange( endY, MngListIntervalX ).getValue() +
      dataSheet.getRange( mngSheet.getRange( endY, MngListIDX ).getValue(), DataCultivationPeriodResultX ).getValue();
  
  RangeClear( mngSheet, MngListIDX, endY, 10, 1 );
  
  // 輪作年限を再設定.
  for ( var y = MngListStartY; y < endY; ++y ) {
    baseNum = mngSheet.getRange( y, MngListCropRotX ).getValue();
    
    if ( baseNum === 0 ) { continue; }// 輪作年限がもともとない作物はスキップ.
    
    tmp = mngSheet.getRange( y, MngListCropRotRestX ).getValue();
    tmp = tmp + num;
    
    // base以上にならないようにする.
    if ( tmp > baseNum ) {
      tmp = baseNum;
    }
    
    mngSheet.getRange( y, MngListCropRotRestX ).setValue( tmp );
  }
作物リストから1つ消す場合は残り輪作年限をもとに戻さないといけません。

改善点など

ソースを見直して、軽くできそうな箇所は軽くしたいですね(;'∀')

ざっくりスケジュールなので、リアルベースでツールを考えてみる?

スケジュールを作成する時の文字色も「define」シートから引っ張っても良いと思います。

…などなど、色々手を加えられそうな部分は山ほどありそうです。

これで一旦完成という事になります!長丁場お疲れ様です。
こんな感じでプログラムと農業、色々とできそうな気がして、考えるだけでワクワクしませんか?(`・ω・´)ノ
私だけか…?



ブログランキング・にほんブログ村へ

Translate

魔王と愉快な仲間

このブログを検索

ラベル

Blogger (7) FX (1) GAS (12) Utility (1) ゲーム (1) ツール (8) ファッション (5) プログラミング (18) まとめ (5) レポート (2) 移住 (3) 育て方 (29) 果菜 (14) 花菜 (2) 覚書 (1) 金策 (9) 茎菜 (3) 根菜 (4) 手法 (1) 多肉 (1) 土壌 (2) 独り言 (2) 読み物 (22) 農業 (3) 農薬 (1) 葉菜 (5)

ブログ アーカイブ

メッセージ

名前

メール *

メッセージ *

Powered by Blogger.

フリー写真素材ぱくたそ

QooQ