github.com/archlabjp/eeslism-go@v0.0.0-20231109122333-4bb7bfcdf292/eeslism/eschdata.go (about)

     1  //This file is part of EESLISM.
     2  //
     3  //Foobar is free software : you can redistribute itand /or modify
     4  //it under the terms of the GNU General Public License as published by
     5  //the Free Software Foundation, either version 3 of the License, or
     6  //(at your option) any later version.
     7  //
     8  //Foobar is distributed in the hope that it will be useful,
     9  //but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
    11  //GNU General Public License for more details.
    12  //
    13  //You should have received a copy of the GNU General Public License
    14  //along with Foobar.If not, see < https://www.gnu.org/licenses/>.
    15  
    16  /*   schdata.c  */
    17  
    18  package eeslism
    19  
    20  import (
    21  	"bufio"
    22  	"fmt"
    23  	"regexp"
    24  	"strconv"
    25  	"strings"
    26  )
    27  
    28  /* 曜日の設定  */
    29  
    30  func Dayweek(fi string, week string, daywk []int, key int) {
    31  	var s string
    32  	var d, id, M, D int
    33  
    34  	var DAYweek = [8]string{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", ""}
    35  
    36  	if key == 0 {
    37  		n, err := fmt.Sscanf(fi, "%d/%d=%s", &M, &D, &s)
    38  		if n != 3 || err != nil {
    39  			panic(err)
    40  		}
    41  	} else {
    42  		re := regexp.MustCompile(`(\d+)/(\d+)=(\S+)`)
    43  		matches := re.FindStringSubmatch(week)
    44  
    45  		if matches != nil && len(matches) == 4 {
    46  			M, _ = strconv.Atoi(matches[1])
    47  			D, _ = strconv.Atoi(matches[2])
    48  			s = matches[3]
    49  		} else {
    50  			panic("WEEKの書式が想定外です")
    51  		}
    52  	}
    53  
    54  	id = 0
    55  	for d = 0; d < 8; d++ {
    56  		if s == DAYweek[d] {
    57  			id = d
    58  		}
    59  	}
    60  	if id == 8 {
    61  		Eprint("<Dayweek>", s)
    62  	}
    63  
    64  	// 開始日と終了日
    65  	ds := FNNday(M, D)
    66  	de := ds + 365
    67  
    68  	// 1日ごとのループ
    69  	for dd := ds; dd < de; dd++ {
    70  		d = dd
    71  		if dd > 365 {
    72  			d = dd - 365
    73  		}
    74  		daywk[d] = id // 曜日の記録
    75  		id++
    76  
    77  		if id > 6 {
    78  			id = 0
    79  		}
    80  	}
    81  
    82  	// `dayweek.efl`から祝日を読み取る
    83  	tokens := NewEeTokens(fi)
    84  
    85  	for {
    86  		s = tokens.GetToken()
    87  		if s == "" || s == ";" {
    88  			break
    89  		}
    90  
    91  		re := regexp.MustCompile(`^(\d+)/(\d+)$`)
    92  		matches := re.FindStringSubmatch(s)
    93  
    94  		if matches != nil && len(matches) == 3 {
    95  			M, _ = strconv.Atoi(matches[1])
    96  			D, _ = strconv.Atoi(matches[2])
    97  			d = FNNday(M, D)
    98  			daywk[d] = 7
    99  		}
   100  	}
   101  }
   102  
   103  /* ------------------------------------------------------------ */
   104  
   105  /*  スケジュ-ル表の入力          */
   106  
   107  // SCHTBデータセットの読み取り
   108  // SCHTBデータセット=一日の設定値、切換スケジュールおよび季節、曜日の指定
   109  // 入力文字列`schtba`を読み取って、 [eeslism.SCHDL]に書き込む
   110  // NOTE: SCHTBデータセットと %s の両方を読み取るために無理が出ている
   111  func Schtable(schtba string, Schdl *SCHDL) {
   112  	var code ControlSWType
   113  
   114  	tokens := NewEeTokens(schtba)
   115  
   116  	for tokens.IsEnd() == false {
   117  		s := tokens.GetToken()
   118  		if s == "*" {
   119  			break
   120  		}
   121  		if s == "\n" || s == ";" {
   122  			continue
   123  		}
   124  
   125  		switch s {
   126  		case "-ssn", "SSN":
   127  			// 季節設定
   128  
   129  			fields := tokens.GetLogicalLine()
   130  			n := len(fields) - 1
   131  
   132  			Sn := SEASN{
   133  				name: fields[0], // 季節名
   134  				N:    n,
   135  				sday: make([]int, n),
   136  				eday: make([]int, n),
   137  			}
   138  
   139  			// 開始日・終了日
   140  			for i := 0; i < n; i++ {
   141  				var Ms, Ds, Me, De int
   142  				fmt.Sscanf(fields[i+1], "%d/%d-%d/%d", &Ms, &Ds, &Me, &De)
   143  				Sn.sday[i] = FNNday(Ms, Ds)
   144  				Sn.eday[i] = FNNday(Me, De)
   145  			}
   146  
   147  			Schdl.Seasn = append(Schdl.Seasn, Sn)
   148  
   149  			break
   150  		case "-wkd", "WKD":
   151  			// 曜日設定
   152  
   153  			fields := tokens.GetLogicalLine()
   154  			n := len(fields) - 1
   155  
   156  			Wk := WKDY{
   157  				name: fields[0], // 曜日名
   158  			}
   159  
   160  			// 対応する曜日のフラグを埋める
   161  			for i := 0; i < n; i++ {
   162  				for j := 0; j < 8; j++ {
   163  					if fields[i+1] == DAYweek[j] {
   164  						Wk.wday[j] = true
   165  						break
   166  					}
   167  				}
   168  			}
   169  
   170  			Schdl.Wkdy = append(Schdl.Wkdy, Wk)
   171  
   172  			break
   173  		case "-v", "VL":
   174  			// 設定値スケジュール定義
   175  			fields := tokens.GetLogicalLine()
   176  			n := len(fields) - 1
   177  
   178  			Dh := DSCH{
   179  				name:  fields[0], // 設定値名
   180  				N:     n,
   181  				stime: make([]int, n),
   182  				etime: make([]int, n),
   183  				val:   make([]float64, n),
   184  			}
   185  
   186  			// 開始時分, 終了時分, 設定値
   187  			Dh.stime = make([]int, n)
   188  			Dh.val = make([]float64, n)
   189  			Dh.etime = make([]int, n)
   190  			for i := 0; i < n; i++ {
   191  				fmt.Sscanf(fields[i+1], "%d-(%f)-%d", &Dh.stime[i], &Dh.val[i], &Dh.etime[i])
   192  			}
   193  
   194  			Schdl.Dsch = append(Schdl.Dsch, Dh)
   195  
   196  			break
   197  		case "-s", "SW":
   198  			// 切替設定スケジュール定義
   199  			fields := tokens.GetLogicalLine()
   200  			n := len(fields) - 1
   201  			nmod := 0
   202  
   203  			Dw := DSCW{
   204  				name:  fields[0], // 切り替え設定名
   205  				N:     n,
   206  				Nmod:  0,
   207  				stime: make([]int, 10),
   208  				etime: make([]int, 10),
   209  				mode:  make([]ControlSWType, 10),
   210  				//dcode: make([]rune, swmx),
   211  			}
   212  
   213  			Dw.stime = make([]int, n)
   214  			Dw.etime = make([]int, n)
   215  			Dw.mode = make([]ControlSWType, n)
   216  			for i := 0; i < n; i++ {
   217  				fmt.Sscanf(fields[i+1], "%d-(%c)-%d", &Dw.stime[i], &code, &Dw.etime[i])
   218  				Dw.mode[i] = ControlSWType(code)
   219  			}
   220  
   221  			// モード数を調べる
   222  			var j int
   223  			for j = 0; j < nmod; j++ {
   224  				if Dw.dcode[j] == code {
   225  					break
   226  				}
   227  			}
   228  
   229  			if j == nmod {
   230  				Dw.dcode[nmod] = code
   231  				nmod++
   232  			}
   233  
   234  			Dw.Nmod = nmod
   235  
   236  			Schdl.Dscw = append(Schdl.Dscw, Dw)
   237  
   238  			break
   239  		}
   240  	}
   241  }
   242  
   243  /* ------------------------------------------------------------ */
   244  
   245  /*  季節、曜日によるスケジュ-ル表の組み合わせ    */
   246  
   247  // SCHNMデータセットの読み取り
   248  // SCHNMデータセット = 季節、曜日によるスケジュ-ル表の組み合わせ
   249  // 入力文字列`schenma`を読み取って、[eeslism.SCHDL]に書き込む
   250  func Schdata(schnma string, dsn string, daywk []int, Schdl *SCHDL) {
   251  	fi := strings.NewReader(schnma)
   252  
   253  	var err error
   254  	const dmax = 366
   255  
   256  	Seasn := Schdl.Seasn
   257  	Wkdy := Schdl.Wkdy
   258  	Dsch := Schdl.Dsch
   259  	Dscw := Schdl.Dscw
   260  
   261  	scanner := bufio.NewScanner(fi)
   262  	for scanner.Scan() {
   263  		line := scanner.Text()
   264  		if line == "*" {
   265  			break
   266  		}
   267  		fields := strings.Fields(line)
   268  
   269  		// 定義の種類
   270  		var dmod rune
   271  		if fields[0] == "-v" || fields[0] == "VL" {
   272  			// 設定値名
   273  			dmod = 'v'
   274  		} else if fields[0] == "-s" || fields[0] == "SW" {
   275  			// 切換設定名
   276  			dmod = 'w'
   277  		} else {
   278  			panic(fields[0])
   279  		}
   280  
   281  		// 年間スケジュールの初期化
   282  		S := SCH{
   283  			name: fields[1], // 設定値名 or 切替設定名
   284  		}
   285  		for d := range S.day {
   286  			S.day[d] = -1
   287  		}
   288  
   289  		// ';' まで繰り返す
   290  		for _, field := range fields[2:] {
   291  
   292  			if field == ";" {
   293  				break
   294  			}
   295  
   296  			var dname string
   297  			var sname string
   298  			var wname string
   299  			is := -1
   300  			var wkday *WKDY = nil
   301  			sc := -1
   302  			sw := -1
   303  
   304  			// 正規表現パターン
   305  			pattern := `^(\w+)(?::(\w*))?(?:-(\w+))?`
   306  			re := regexp.MustCompile(pattern)
   307  
   308  			// マッチする部分を取り出す
   309  			match := re.FindStringSubmatch(field)
   310  
   311  			// 3つの部分を取り出し
   312  			if len(match) >= 2 {
   313  				// ex) `TrsetC`
   314  				dname = match[1] // 参照する1日の設定名
   315  			}
   316  			if len(match) >= 3 {
   317  				// ex) `TrsetH:Winter`
   318  				sname = match[2] // 季節設定名 ex)Summer
   319  			}
   320  			if len(match) >= 4 {
   321  				// ex) `ACSWLDwd:Summer-Weekday`
   322  				// ex) `ACSWLDwd:-Weekday`
   323  				wname = match[3] // 曜日設定名 ex)Weekday
   324  			}
   325  
   326  			if sname != "" {
   327  				// 季節設定の検索
   328  				is, err = idssn(sname, Seasn)
   329  				if err != nil {
   330  					panic(err)
   331  				}
   332  			}
   333  			if wname != "" {
   334  				// 曜日設定の検索
   335  				iw, err := idwkd(wname, Wkdy)
   336  				if err != nil {
   337  					panic(err)
   338  				}
   339  				wkday = &Wkdy[iw]
   340  			}
   341  			if dname != "" {
   342  				if dmod == 'v' {
   343  					// 一日の設定値スケジュ-ルの検索
   344  					sc, err = iddsc(dname, Dsch)
   345  					if err != nil {
   346  						panic(err)
   347  					}
   348  				} else if dmod == 'w' {
   349  					// 一日の切り替えスケジュ-ルの検索
   350  					sw, err = iddsw(string(dname), Dscw)
   351  					if err != nil {
   352  						panic(err)
   353  					}
   354  				} else {
   355  					panic(dmod)
   356  				}
   357  			}
   358  
   359  			// ループ回数
   360  			var N int
   361  			if is >= 0 {
   362  				// ** 季節設定がある場合 **
   363  				N = Seasn[is].N
   364  			} else {
   365  				// ** 季節設定がない場合 **
   366  				N = 1
   367  			}
   368  
   369  			// 年間スケジュールの作成ループ
   370  			for k := 0; k < N; k++ {
   371  				var ds, de int
   372  				if is >= 0 {
   373  					// ** 季節設定がある場合 **
   374  					// ex) `TrsetC:Winter`
   375  					// ex) `ACSWLDwd:Winter-Weekday`
   376  					Sn := Seasn[is]
   377  					ds = Sn.sday[k] //開始日
   378  					de = Sn.eday[k] //終了日
   379  
   380  					if ds > de {
   381  						de += 365 // 年末跨ぎ
   382  					}
   383  				} else {
   384  					// ** 季節設定がない場合場合 **
   385  					// ex) `TrsetC`
   386  					// ex) `ACSWLDwd:-Weekday`
   387  					ds = 1    // 開始日 NOTE: 配列インデックス1-366を想定 (Fortran譲りか)
   388  					de = dmax // 終了日
   389  				}
   390  
   391  				for day := ds; day <= de; day++ {
   392  					d := day
   393  					if day > 365 {
   394  						d = day - 365 // NOTE: d=1に戻る条件になっている
   395  					}
   396  
   397  					// 曜日指定が無い or 指定曜日であることを確認
   398  					if wkday == nil || wkday.wday[daywk[d]] {
   399  						if dmod == 'v' {
   400  							S.day[d] = sc
   401  						} else if dmod == 'w' {
   402  							S.day[d] = sw
   403  						} else {
   404  							panic(dmod)
   405  						}
   406  					}
   407  				}
   408  			}
   409  		}
   410  
   411  		// 年間スケジュールに追加
   412  		if dmod == 'v' {
   413  			Schdl.Sch = append(Schdl.Sch, S)
   414  		} else if dmod == 'w' {
   415  			Schdl.Scw = append(Schdl.Scw, S)
   416  		} else {
   417  			panic(dmod)
   418  		}
   419  	}
   420  
   421  	// Val, Isw の領域確保
   422  	Schdl.Val = make([]float64, len(Schdl.Sch))
   423  	Schdl.Isw = make([]ControlSWType, len(Schdl.Scw))
   424  }
   425  
   426  /* ------------------------------------------------------------ */
   427  
   428  /*  季節、曜日によるスケジュ-ル表の組み合わせ名へのスケジュ-ル名の追加  */
   429  
   430  func Schname(schdl *SCHDL) {
   431  	// 年間一定のスケジュールを追加
   432  	for i, sc := range schdl.Dsch {
   433  		sch := SCH{
   434  			name: sc.name,
   435  		}
   436  		for d := range sch.day {
   437  			sch.day[d] = i
   438  		}
   439  
   440  		schdl.Sch = append(schdl.Sch, sch)
   441  	}
   442  
   443  	// 年間一定のスケジュールを追加
   444  	for j, sw := range schdl.Dscw {
   445  		scw := SCH{
   446  			name: sw.name,
   447  		}
   448  		for d := range scw.day {
   449  			scw.day[d] = j
   450  		}
   451  
   452  		schdl.Scw = append(schdl.Scw, scw)
   453  	}
   454  
   455  	// Val, Isw の領域確保
   456  	schdl.Val = make([]float64, len(schdl.Sch))
   457  	schdl.Isw = make([]ControlSWType, len(schdl.Scw))
   458  }