見出し画像

「実践SNMP教科書」復刻のために気象情報を取得するプログラムを作った

今朝は4時に起きました。新人助手の猫さんは、後から起きてきました。

さて、「実践SNMP教科書」の復刻をしていますが、大きな問題に直面しました。拡張MIBモジュールの対象となっている気象観測装置を捨ててしまったことです。

気象観測装置

代わりに、

を使うことにしました。

の記事でも使ったものです。
とりあえず、取得するためのテストプログラムを作ってみました。
APIキーと位置をパラメータで指定っして気象情報を取得します。

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

type OpenWeather struct {
	Coord struct {
		Lon float64 `json:"lon"`
		Lat float64 `json:"lat"`
	} `json:"coord"`
	Weather []struct {
		ID          int    `json:"id"`
		Main        string `json:"main"`
		Description string `json:"description"`
		Icon        string `json:"icon"`
	} `json:"weather"`
	Base string `json:"base"`
	Main struct {
		Temp      float64 `json:"temp"`
		FeelsLike float64 `json:"feels_like"`
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  int     `json:"pressure"`
		Humidity  int     `json:"humidity"`
		SeaLevel  int     `json:"sea_level"`
		GrndLevel int     `json:"grnd_level"`
	} `json:"main"`
	Visibility int `json:"visibility"`
	Wind       struct {
		Speed float64 `json:"speed"`
		Deg   int     `json:"deg"`
		Gust  float64 `json:"gust"`
	} `json:"wind"`
	Clouds struct {
		All int `json:"all"`
	} `json:"clouds"`
	Dt  int `json:"dt"`
	Sys struct {
		Type    int    `json:"type"`
		ID      int    `json:"id"`
		Country string `json:"country"`
		Sunrise int    `json:"sunrise"`
		Sunset  int    `json:"sunset"`
	} `json:"sys"`
	Timezone int    `json:"timezone"`
	ID       int    `json:"id"`
	Name     string `json:"name"`
	Cod      int    `json:"cod"`
}

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("openweather <apkey> <loc>")
	}
	url := "https://api.openweathermap.org/data/2.5/weather?q=" + os.Args[2] + "&units=metric&APPID=" + os.Args[1]
	r, err := http.Get(url)
	if err != nil {
		log.Fatalln(err)
	}
	defer r.Body.Close()
	b, err := io.ReadAll(r.Body)
	if err != nil {
		log.Fatalln(err)
	}
	data := new(OpenWeather)
	if err := json.Unmarshal(b, data); err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%d %d %d %d %d %d\n", data.Dt, int64(data.Main.Temp*10), data.Main.Humidity, data.Main.Pressure, data.Wind.Deg, int64(data.Wind.Speed))
}

OpenWeatherのデータ型は、サーバーの応答のJSONから

を使って作りました。

{"coord":{"lon":139.5577,"lat":35.8578},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":5.37,"feels_like":2.04,"temp_min":3.68,"temp_max":7.75,"pressure":1022,"humidity":47,"sea_level":1022,"grnd_level":1021},"visibility":10000,"wind":{"speed":4.5,"deg":339,"gust":7.69},"clouds":{"all":55},"dt":1702843951,"sys":{"type":2,"id":2001249,"country":"JP","sunrise":1702849566,"sunset":1702884595},"timezone":32400,"id":6940394,"name":"Fujimi","cod":200}

を入れれば、

type AutoGenerated struct {
	Coord struct {
		Lon float64 `json:"lon"`
		Lat float64 `json:"lat"`
	} `json:"coord"`
	Weather []struct {
		ID          int    `json:"id"`
		Main        string `json:"main"`
		Description string `json:"description"`
		Icon        string `json:"icon"`
	} `json:"weather"`
	Base string `json:"base"`
	Main struct {
		Temp      float64 `json:"temp"`
		FeelsLike float64 `json:"feels_like"`
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  int     `json:"pressure"`
		Humidity  int     `json:"humidity"`
		SeaLevel  int     `json:"sea_level"`
		GrndLevel int     `json:"grnd_level"`
	} `json:"main"`
	Visibility int `json:"visibility"`
	Wind       struct {
		Speed float64 `json:"speed"`
		Deg   int     `json:"deg"`
		Gust  float64 `json:"gust"`
	} `json:"wind"`
	Clouds struct {
		All int `json:"all"`
	} `json:"clouds"`
	Dt  int `json:"dt"`
	Sys struct {
		Type    int    `json:"type"`
		ID      int    `json:"id"`
		Country string `json:"country"`
		Sunrise int    `json:"sunrise"`
		Sunset  int    `json:"sunset"`
	} `json:"sys"`
	Timezone int    `json:"timezone"`
	ID       int    `json:"id"`
	Name     string `json:"name"`
	Cod      int    `json:"cod"`
}

を作ってくれます。かなり便利です。


うまく行ったので、データ取得を定期的に実行するプログラムにしてみました。

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

type OpenWeather struct {
	Coord struct {
		Lon float64 `json:"lon"`
		Lat float64 `json:"lat"`
	} `json:"coord"`
	Weather []struct {
		ID          int    `json:"id"`
		Main        string `json:"main"`
		Description string `json:"description"`
		Icon        string `json:"icon"`
	} `json:"weather"`
	Base string `json:"base"`
	Main struct {
		Temp      float64 `json:"temp"`
		FeelsLike float64 `json:"feels_like"`
		TempMin   float64 `json:"temp_min"`
		TempMax   float64 `json:"temp_max"`
		Pressure  int     `json:"pressure"`
		Humidity  int     `json:"humidity"`
		SeaLevel  int     `json:"sea_level"`
		GrndLevel int     `json:"grnd_level"`
	} `json:"main"`
	Visibility int `json:"visibility"`
	Wind       struct {
		Speed float64 `json:"speed"`
		Deg   int     `json:"deg"`
		Gust  float64 `json:"gust"`
	} `json:"wind"`
	Clouds struct {
		All int `json:"all"`
	} `json:"clouds"`
	Dt  int `json:"dt"`
	Sys struct {
		Type    int    `json:"type"`
		ID      int    `json:"id"`
		Country string `json:"country"`
		Sunrise int    `json:"sunrise"`
		Sunset  int    `json:"sunset"`
	} `json:"sys"`
	Timezone int    `json:"timezone"`
	ID       int    `json:"id"`
	Name     string `json:"name"`
	Cod      int    `json:"cod"`
}

func main() {
	if len(os.Args) < 3 {
		log.Fatalf("openweather <apkey> <loc>")
	}
	url := "https://api.openweathermap.org/data/2.5/weather?q=" + os.Args[2] + "&units=metric&APPID=" + os.Args[1]
	lastDt := 0
	hist := []*OpenWeather{}
	for {
		data, err := getData(url)
		if err != nil {
			log.Println(err)
			time.Sleep(time.Minute)
			continue
		}
		if data.Dt != lastDt {
			lastDt = data.Dt
			o := []string{}
			o = append(o, fmt.Sprintf("0 %d %d %d %d %d %d", data.Dt, int64(data.Main.Temp*10), data.Main.Humidity, data.Main.Pressure, data.Wind.Deg, int64(data.Wind.Speed)))
			if len(hist) > 256 {
				hist = hist[1:]
			}
			hist = append(hist, data)
			for i, l := range hist {
				o = append(o, fmt.Sprintf("%d %d %d %d %d %d %d", i+1, l.Dt, int64(l.Main.Temp*10), l.Main.Humidity, l.Main.Pressure, l.Wind.Deg, int64(l.Wind.Speed)))

			}
			os.WriteFile("/tmp/owd.txt", []byte(strings.Join(o, "\n")+"\n"), 0666)
		}
		time.Sleep(time.Minute * 1)
	}

}

func getData(url string) (*OpenWeather, error) {
	r, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer r.Body.Close()
	b, err := io.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}
	data := new(OpenWeather)
	if err := json.Unmarshal(b, data); err != nil {
		return nil, err
	}
	return data, nil
}

ちゃんと取得できているようです。

# cat /tmp/owd.txt
0 1702935005 43 58 1026 360 2
1 1702930175 39 57 1026 354 3
2 1702930835 39 57 1026 354 3
3 1702931437 40 57 1026 352 3
4 1702932038 40 57 1026 352 3
5 1702932821 42 58 1026 352 3
6 1702933181 43 57 1026 352 3
7 1702933805 42 58 1026 352 3
8 1702934625 42 58 1026 352 3
9 1702935005 43 58 1026 360 2

外は4.2℃です。寒い
今朝は時間切れです。明日、このデータを読み込んで値を返す拡張MIBのエージェントを作ろうと思います。

ここから先は

0字
SNMPの仕様について解説した本やサイトは、沢山あると思います。 独自の拡張MIBを自分で設計してMIBファイルやエージェントを作る方法を解説した教科書はないと思います。

20年近く前に書いた「実践SNMP教科書」を現在でも通用する部分だけ書き直して復刻するマガジンです。最近MIBの設計で困っている人に遭遇し…

開発のための諸経費(機材、Appleの開発者、サーバー運用)に利用します。 ソフトウェアのマニュアルをnoteの記事で提供しています。 サポートによりnoteの運営にも貢献できるのでよろしくお願います。