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

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

魔王と愉快な仲間

このブログを検索

ラベル

ブログ アーカイブ

メッセージ

名前

メール *

メッセージ *

Powered by Blogger.

フリー写真素材ぱくたそ

QooQ