HAMADAの語り草

興味のある技術のアウトプットをしたいと思います

githubの草を取得してみた話 (エンジニア版たま○っちを作る準備)

 はじめに

こんにちは!!!会津大学学部二年のHAMADAです。 このブログはA-PxL Advent Calendar 2022 24日目のブログです。今日は、クリスマスイブということで、暇な私はせっせこ開発に勤しみたいと思います。開発できる時間がクリスマスプレゼントですね(泣) 張り切っていきましょー!!!

さて、今回は githubの草を取得してみた話です。

目的・動機

ふとGithubのcontribute数を使ってエンジニア版た○ごっちみたいなものが作れないかなと思い立ちました。フロント部分はUnityを使ってARもしくは単純なゲームっぽくしたかったのですが、ブログが長くなりそうなの(言い訳)と間に合わなかった(本当の理由)ので今回はgithubから草を取得した話を書きます。

使用したもの

  • Go

  最近よく書く言語。

  みなさんご存知github。今年もお世話になりました。

  バッテリーが限界を迎えています。

Githubのcontribute数を取得する

URLを読み込む

やったことは大きく2つ

  • .envファイルの作成
    • githubにコードをあげるので、後々のことを考えて一応.envから読み取るようにしました。
  • .envファイルを読み込めるように関数を作成する
    • 単純に.envからurlを読み取って返すだけ。

コードは以下のようです。

//.env
GrassURL="https://github.com/users/{user名}/contributions"
//main.go 

package main

import (
    "fmt"
    "os"
    "github.com/joho/godotenv"
)

func main(){
        var grassURL string = loadEnvURL()
}

func loadEnvURL() string {
    err := godotenv.Load(".env")

    if err != nil {
        fmt.Printf(".envの読み込みに失敗しました: %v", err)
    }

    url := os.Getenv(("GrassURL"))

    return url
}

取得したsvgから日付とcontribute数を取得する

やったことは大きく二つ

  • svgの中から正規表現を使って日付とcontribute数の要素を抜き出す。
  • 任意の日にち(今回は今の日にち)を指定して、その日のcontribute数を取得する。

コードは以下のようです。

//main.go 今のフェーズで実装した部分
package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "strconv"
    "strings"
    "time"
    "github.com/joho/godotenv"
)

const layout = "2006-01-02"

func main() {

    var grassURL string = loadEnvURL()

    data_date, data_count := getGrass(grassURL)

    for i := 0; i < len(data_count); i++ {
        t := time.Now()
        if t.Format(layout) == data_date[i] {
            fmt.Printf("this is data_date %s \ndata count is %d \n", t.Format(layout), data_count[i])
        }

    }
}

func getGrass(url string) ([500]string, [500]int) {
    var data_count [500]int
    var data_date [500]string
    res, err := http.Get(url)
    if err != nil {
        fmt.Println("urlの取得がうまくできませんでした")
    }
    defer res.Body.Close()

    body, _ := io.ReadAll(res.Body)

    regex1 := regexp.MustCompile(`data-count`)
    regex2 := regexp.MustCompile(`data-date`)
    regex3 := regexp.MustCompile(`ry="2" `)

    result := regex3.Split(string(body), -1)
    for i := 0; i < len(result); i++ {
        arr := strings.Split(result[i], " ")

        var temp_arr []string

        for j := 0; j < len(arr); j++ {
            if regex2.MatchString(arr[j]) {
                temp_arr = strings.Split(arr[j], "\"")
                data_date[i] = temp_arr[1]

            } else if regex1.MatchString(arr[j]) {
                temp_arr = strings.Split(arr[j], "\"")
                num, _ := strconv.Atoi(temp_arr[1])
                data_count[i] = num
            }

        }
    }
    return data_date, data_count
}

正規表現を使って、とてつもなく長いsvgを区切って利用しています。これは、全体を眺めてから、区切れそうなところで区切るという力こそパワーな実装なのでもう少し上手くできるような気はします。 data-date, data-countを抜き出してからは、欲しい"2022-01-02"のような形式とcommit数だけを抜き出しています。 文字列data-countの時だけ整数値にして返しています。

完成コードと挙動

コードは以下のようです。

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "regexp"
    "strconv"
    "strings"
    "time"

    "github.com/joho/godotenv"
)

const layout = "2006-01-02"

func main() {

    var grassURL string = loadEnvURL()

    data_date, data_count := getGrass(grassURL)

    for i := 0; i < len(data_count); i++ {
        t := time.Now()
        if t.Format(layout) == data_date[i] {
            fmt.Printf("this is data_date %s \ndata count is %d \n", t.Format(layout), data_count[i])
        }

    }
}

func loadEnvURL() string {
    err := godotenv.Load(".env")

    if err != nil {
        fmt.Printf(".envの読み込みに失敗しました: %v", err)
    }

    url := os.Getenv(("GrassURL"))

    return url
}

func getGrass(url string) ([500]string, [500]int) {
    var data_count [500]int
    var data_date [500]string
    res, err := http.Get(url)
    if err != nil {
        fmt.Println("urlの取得がうまくできませんでした")
    }
    defer res.Body.Close()

    body, _ := io.ReadAll(res.Body)

    regex1 := regexp.MustCompile(`data-count`)
    regex2 := regexp.MustCompile(`data-date`)
    regex3 := regexp.MustCompile(`ry="2" `)

    result := regex3.Split(string(body), -1)
    for i := 0; i < len(result); i++ {
        arr := strings.Split(result[i], " ")

        var temp_arr []string

        for j := 0; j < len(arr); j++ {
            if regex2.MatchString(arr[j]) {
                temp_arr = strings.Split(arr[j], "\"")
                data_date[i] = temp_arr[1]

            } else if regex1.MatchString(arr[j]) {
                temp_arr = strings.Split(arr[j], "\"")
                num, _ := strconv.Atoi(temp_arr[1])
                data_count[i] = num
            }

        }
    }
    return data_date, data_count
}

挙動

感想と展望

なんとか草を取得することができてよかったです。コード自体はそんなに長くなく終わってみれば簡単なのですが、どこから情報を引っ張ってくるか、正規表現の使い方があやふやといった問題があり中身の割には苦戦しました。だんだん自分でやりたいことが、調べながら実装するということができるようになってきていて嬉しい気持ちもあります。続きも実装して、簡単なアプリにしたいなと考えています。ここまで読んでいただきありがとうございました。何かの参考になれば幸いです。次回またよろしくお願いいたします!!!Merry Christmas!

所属サークル

A-PxL@C101土曜西こ44b (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

参考リンク

taroosg.io

ashitani.jp

お風呂の水位センサーを作るために、ラズパイからLINEに通知できるようにしてみた話

はじめに

こんにちは!!!会津大学学部二年のHAMADAです。 このブログはAizu Advent Calendar 2022 17日目のブログです。 会津では、雪も積もり始めました。寒いのでお風呂にゆっくり浸かりたいものです。

さて、今回はラズパイとLINEの連携の話です。

目的・動機

お風呂が溜まったことをLINEに通知してくれたら嬉しいなぁとずっと思っているので、足掛かりとなるシステムを作ってみました。 実際に運用できるようにもしたいですね。

使用したもの

  • Raspberry Pi 4 Model B 4GB

    供給がそろそろ追いつくみたいですね。

  • 土壌センサー

    水があるか無いかを調べるのには十分すぎる精度でした。

www.amazon.co.jp

  • MCP3008

    ADコンバータです。

akizukidenshi.com

  • LINE Notify

    LINE Botより汎用性には乏しいが通知する分には十分

  • Python

    インデントがぁぁぁぁ。ツールとしては便利だと思います。

  • 百均のペン立て

    水を貯めるのにちょうど良くて便利でした。

    湿潤センサーのデータを取れるようにする

セットアップ

interface wlan0
static ip_address=192.168.##.###/24
static routers=192.168.0.1

という風に設定する

GUIを使える場合は、ラズパイのネットワークマークを右クリックしてWireless & Wired Network Settings… からIPv4 Address と Routerをセットします。こっちの方が簡単にできる。

配線

データシートを見ながらやりました。

http://akizukidenshi.com/download/ds/microchip/mcp3008.pdf

見た目はこんな感じ

SPI通信をするための設定

$ sudo raspi-config を実行する

書いたコード

import time
import spidev

Vref = 3.334  

spi = spidev.SpiDev()

spi.open(0, 0)  
spi.max_speed_hz = 100000 

def readAdc(channel):
    adc = spi.xfer2([1, (8 + channel) << 4, 200])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data

def convertVolts(data, vref):
    mois = (data * vref) / float(1023)
    return mois

if __name__ == '__main__':
    try:
        while True:
            data = readAdc(channel=0)
            mois = convertVolts(data, Vref)
            print("CH0 volts: {:.2f}".format(mois))

            time.sleep(2)

    except KeyboardInterrupt:
        spi.close()

LINEとの連携

今回はLINE Notifyを用いて、水が湿潤センサーに触れるとお湯が溜まったことを通知してくれるようにした。

LINE Notifyの設定

https://notify-bot.line.me/ja/にアクセスしてログインします。

右上の自分の名前>マイページを選択します。

トークンを発行するを押します。

アクセストークンは一度しか表示されないので必ずコピーしましょう。

実行するコードの作成

以下のようなコードになりました

# -*- coding:utf-8 -*-
import time
import spidev
import requests

import os

from dotenv import load_dotenv
load_dotenv()

# 発行されたトークンID
ACCESS_TOKEN =os.environ['ACCESS_TOKEN']
STR_NOTICE_MESSAGE = 'お湯張りができました'

Vref = 3.334 

spi = spidev.SpiDev()

spi.open(0, 0) 
spi.max_speed_hz = 100000  

def readAdc(channel):
    adc = spi.xfer2([1, (8 + channel) << 4, 200])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data

def convertVolts(data, vref):
    mois = (data * vref) / float(1023)
    return mois

def fnc_line_notify():
    line_notify_api  =  "https://notify-api.line.me/api/notify"
    headers = {"Authorization": f"Bearer {ACCESS_TOKEN}"}
    data = {'message': f'{STR_NOTICE_MESSAGE}'}
    requests.post(line_notify_api, headers=headers, data=data)
    return

if __name__ == '__main__':
    try:
        while True:
            data = readAdc(channel=0)
            mois = convertVolts(data, Vref)
            print("CH0 mois: {:.2f}".format(mois))
            if mois < 1.8:
                fnc_line_notify()
                break
            time.sleep(2)

    except KeyboardInterrupt:
        spi.close()

.envファイルを同じディレクトリ内に作成し、ACCESS_TOKENとして保存しています。

load_dotenv()を使用し、os.environ['ACCESS_TOKEN']で取得しています。

moisが1.8を下回った時にfnc_line_notify() を呼び出して、LINEに通知する処理を行っています。

moisというのは、実際には電圧で湿潤センサーを水につけた際に電圧が1.8以下になっていたのでこういった扱いにしています。

デモ

例に倣って、gifを用いていますので見にくいところがあると思いますがご容赦ください。

感想と展望

LINE Notifyを使うのは初めてでしたが、LINE Botよりも簡単にできる印象を受けました。センサーを使った開発などをきちんとやったことがなかったのでとてもやっていて新鮮でした。実際にお風呂で使えるように、電源を電池にしてみたり、水からラズパイを守ったりとまだまだ改良の余地があるので頑張りたいと思います。ここまで読んでいただきありがとうございました。何かの参考になれば幸いです。次回またよろしくお願いいたします!!!

所属サークル

A-PxL@C101土曜西こ44b (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

参考リンク

101010.fun

notify-bot.line.me

AWS CLIを簡単に使えるようにしたのでメモを残したい

はじめに

こんにちは!!会津大学学部二年のHAMADAです。

今年もあと1ヶ月を切りました。今年最後のテストも終わったので、最後まで走り切るためにブログを今月は頑張ろうと思います。 Advent Calenderの関係であと二本は頑張って書きます!!!

今回は、AWS CLIについてです。

目的・動機

AWSの記事を読んでいるとコマンドラインで、作業している記事が多かったからという安直な理由で取り組みました。(ブログのネタがなくて、苦し紛れっていうのも多少ありますが.... )

月並みではありますが、自分自身の理解を深めるためにまとめてみました。

環境

簡単にセッティング

  • brew install awscli を実行。
  • aws --verion でversionを確認。
  • aws configure
$ aws configure
AWS Access Key ID [None]:AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]:###################
Default region name [None]:ap-northeast-1
Default output format [None]:json 
  • これらの情報は~/.aws/credentialsと~/.aws/configに保存される。

~/.aws/credentials

[default] aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=##################

~/.aws/config

[default] region=ap-northeast-1 
output=json

これで最低限は設定できた。

configure コマンド

  • aws configure set を利用して任意の認証情報または構成設定を設定できる。

    • aws configure set region ap-northeast-1 --profile integ

      これはregionという名前のプロファイルの中にある integ 設定している。

      設定を削除するためには、空白の文字列を値として渡すか、テキストエディタで直接編集する。

  • aws configure get を利用して設定した認証情報または構成設定を取得する。

    • aws configure get region --profile integ

           

  • aws configure import を利用するとCSVウェブコンソールから生成されたAWS認証情報をインポートする。

    • CSVファイルには三つのヘッダーが含まれている必要がある。
      • ユーザ名
      • アクセスキーID
      • シークレットアクセスキー
    • aws configure import --csv file://credentials.csv を実行すればimportできる。
  • aws configure list を利用すると、設定した名前、値、設定を取得し、表示する。

  • aws configure list-profiles を利用するとすべてのプロファイル一覧が表示される。

configの設定

今回は、個人的に今使用する必要がありそうなものを設定。

  • AWSアクセスキー
    • これはcredentialsファイルに書く(今回は、簡単にセッティングで設定済み)
    • aws_access_key_id = AKIAIOSFODNN7EXAMPLE
  • AWSシークレットアクセスキー
    • これはcredentialsファイルに書く(今回は、簡単にセッティングで設定済み)
    • aws_secret_access_key = #####################
  • CLI自動プロンプト
    • awsコマンドの補完や検索などをやってくれる。
    • cli_auto_prompt = on
    • onの部分をon-partial にするとすべてのコマンドではなく不慣れなコマンドだけ補完するようにできる。
  • AWS history
    • aws history list コマンドを使えるようになる(defaultではoffになっている)
    • cli_history = enabled にすると使える。
      • コマンドの履歴がない場合は "Could not locate history. Make sure cli_history is set to enabled in the ~/.aws/config file" と出る。

名前付きプロファイル

  • 認証情報プロファイル ~/.aws/credentials

    • [default] はプロファイルなしで実行するときに使う。

    • [user1] (別のプロファイル)を使用するときは--profile user1 のようにコマンドで指定。

      • 注意:credentialsファイルにエントリは作成するときは’profile’という単語は使わない。
    • 記入例

      [default] aws_access_key_id=AKIAIOSFODNN7EXAMPLE 
      aws_secret_access_key=#####################################
      [user1] aws_access_key_id=AKIAI44QH8DHBEXAMPLE 
      aws_secret_access_key=#####################################
  • 設定プロファイル ~/.aws/config

    • 各プロファイルには異なる認証情報を記入できる。(IAMが違うなど)

    • 記入例

      [default] region=ap-northeast-1 
      output=json
      [profile user1] region=us-east-1 
      output=text
      • 注意[profile user1]のようにprofileを書くところがcredentialsとの違い。

環境変数の設定

  • 環境変数を設定するとシェルセッションが終了する、もしくは、変数に別の値を設定するまで使用する値を変更することができる。

  • 名前付きプロファイルを一時的にデフォルトにしたいときなどに利用する。

  • 利用例

    $ export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE 
    $ export AWS_SECRET_ACCESS_KEY=##############################
    $ export AWS_DEFAULT_REGION=ap-northeast-1
  • 他にも多くのサポートされる環境変数がある。

AWS CLI を設定する環境変数

IAM確認

  • IAMユーザの確認

    • aws iam list-users

      {
          "Users": [
              {
                  "Path": "#",
                  "UserName": "#########",
                  "UserId": "######################",
                  "Arn": "arn:aws:iam::################:####/########",
                  "CreateDate": "########################",
                  "PasswordLastUsed": "######################"
              }
          ]
      }
      
  • IAMグループの確認

    • aws iam list-groups

    • {
          "Groups": [
              {
                  "Path": "#",
                  "GroupName": "######",
                  "GroupId": "####################",
                  "Arn": "arn:aws:iam::############:######/#######",
                  "CreateDate": "######################"
              }
          ]
      }
  • シークレットアクセスキーのコピーに失敗していて、

    An error occurred (SignatureDoesNotMatch) when calling the ListGroups operation: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

    が出ました。個人的に注意。

感想と展望

基本的には、普段ターミナルを触るのと差異がないように感じました。要所要所でセキュリティを意識することが多く、AWSだけでなく他のクラウドサービスもそうですが、セキュリティへの意識はしっかり持って触るべきだと感じました。セキュリティ対策の部分が甘いのでしっかり学習したいと思います。何かの参考になれば幸いです。間違った点などあれば、ご指摘いただけると嬉しいです。ここまで読んでいただきありがとうございました。

 

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

 

参考リンク

docs.aws.amazon.com

AWSのハンズオンをGoで書いて入門した話 ~翻訳APIを作ったよ~

はじめに

こんにちは!!会津大学学部二年のHAMADAです。

11月も後半に差し掛かり、寒さに苦しんでいる今日この頃です。

今回は、AWSのハンズオンGoで書いて入門したお話です。

動機・目的

今までAWSGCP、Firebaseなどに対する得体の知れなさを持っていましたが、私が作りたいものや、学びたい技術とは切っても切れないものなので思い切って挑戦してみました!!  足りないポイント、間違った解釈があるかと思いますが、暖かい目でみていただけると嬉しいです。

使った技術

  • AWS
    Amazon Web Service 色々できるやつ。今回使ったのは以下

    • Lambda
      関数をAWS上で実行できる。
    • API Gateway
      APIの管理、配布、保守などを目的としたもの。繋ぎ合わせ
    • DynamoDB
      NoSQLのDB  jsonのやつ。今回これで苦しみました。
  • Go
    最近よく書く言語。try catchではなくerror処理ができるところが好き。

参考にしたハンズオンリンク

pages.awscloud.com

手順

  1. AWS Lambdaを構築、Amazon Translateを呼び出す。
  2. API Gateway を用いて Lambda Function と組み合わせてWeb APIを作成する。
  3. Amazom DynamoDB を用いて翻訳したデータを保存する。

1. AWS Lambdaを構築、Amazon Translateを呼び出す

awsにログインして、Lamdaを開きます。

アカウントの作成方法やセキュリティの設定などは機会があれば書こうと思います。

オレンジの関数の作成を押します。

今回は1から作成を選択し、関数名を自分で記入しランタイムをGo 1.xにしました。

pythonやNode.jsではAWS上でプログラムが書けるのですが、Goは対応していないのでvscodeでプログラムを書いてfunction.zipファイルをアップロードします。

書いたコード

//Translate/transltate.go
package translate
    import (
	"fmt"
	"log"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/translate"
)


 func TranslateFunc() {
 	InputText := "日本語だよ!こっちは英語"
 	InputLang := "ja"
 	OutputLang := "en"
 	sess := session.Must(session.NewSession())
 	trans := translate.New(sess)

 	result, err := trans.Text(&translate.TextInput{
 		SourceLanguageCode: aws.String(InputLang),
 		TargetLanguageCode: aws.String(OutputLang),
 		Text:               aws.String(InputText),
 	})
 	if err != nil {
 		log.Print("おかしいぜ")
 	}

 	fmt.Println(*result.TranslatedText)
 }
//handler.go
package main

import (
	"github.com/ahmos0/goLambdaFirst.git/Testgreeting"
	"github.com/ahmos0/goLambdaFirst.git/Translate"
	"github.com/aws/aws-lambda-go/lambda"
)

func exucuteFunc() {
        Translate.TranslateFunc()
}

func main() {
	lambda.Start(exucuteFunc)
}

InputTextの日本語を英語に変換します。InputLangは今回日本語なので"ja"。これをOutputLangは"en"にして変換しています。

その後Translateのセッションを確立しています。

trans.Textに翻訳の対象となる言語(今回は日本語)、翻訳先の言語(今回は英語)、対象となる文字列を translate.TextInput で翻訳与えてあげます。

handler.goから実行します。

Lambda上にzipファイルとしてあげる際に、以下を実行しました。

go build
GOOS=linux go build -o handler handler.go
zip function.zip handler 

普段通りbuildしてあげてから、handlerを作成します。これは、Lambda関数で呼び出す主軸のようなものですね。その後、handlerをzipにしてあげればOKです。

テストのやり方などは、最終checkの段階に書こうと思います。

2. API Gateway を用いて Lambda Function と組み合わせてWeb APIを作成する

右上のAPIを作成から作ります。

REST APIを選びます。

設定は上記のようにしました。

 

リソースの作成から/translate

メソッドの作成からGETを作成します。

GETの統合リクエストを選択し、以下のように設定。

書いたコード

//Translate/translate.go
package Translate

import (
	"fmt"
	"log"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/translate"
)

var OutputText string

func TranslateFunc(req events.APIGatewayProxyRequest) string {
	InputText := req.QueryStringParameters["InputText"]
	InputLang := "ja"
	OutputLang := "en"
	sess := session.Must(session.NewSession())
	trans := translate.New(sess)

	log.Println(InputText + "これです")

	result, err := trans.Text(&translate.TextInput{
		SourceLanguageCode: aws.String(InputLang),
		TargetLanguageCode: aws.String(OutputLang),
		Text:               aws.String(InputText),
	})
	if err != nil {
		log.Print("おかしいぜ")
	}

	OutputText := *result.TranslatedText

	return OutputText
}

//handler.go
package main

import (
	"github.com/ahmos0/goLambdaFirst.git/Testgreeting"
	"github.com/ahmos0/goLambdaFirst.git/Translate"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
)

type Response struct {
	StatusCode int    `json:"statusCode"`
	Body       string `json:"body"`
}

func ExucuteFunc(request events.APIGatewayProxyRequest) (Response, error) {
	OutputText := Translate.TranslateFunc(request)
	return Response{
		StatusCode: 200,
		Body:       OutputText,
	}, nil
}

func main() {
	lambda.Start(ExucuteFunc)
}

events.APIGatewayProxyRequestで文字列を確保し、Translate.TranslateFuncで翻訳しています。QueryStringParamerters["InputText"]で元の日本語文を取得しています。

できているかの確認は、今回も最後のチェックで説明します。

 

3. Amazom DynamoDB を用いて翻訳したデータを保存する

DynamoDBでテーブル作成をします.

右上のテーブル作成から作ります。

テーブルの作成は以下のように設定しました。

テーブルの項目は、timestamp、InputText、OutputTextを作りました。

書いたコード

//database/DynamoDB.go
package database

import (
	"fmt"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
)

type Item struct {
	TimeStamp   string `json:"timestamp" dynamodbav:"timestamp"`
	InputTextj  string `json:"inputtext" dynamodbav:"InputText"`
	OutputTextj string `json:"outputtext" dynamodbav:"OutputText"`
}

func OperateDB(InputText string, OutputText string) {
	sess := session.Must(session.NewSessionWithOptions(session.Options{
		SharedConfigState: session.SharedConfigEnable,
	}))
	fmt.Println(InputText)
	fmt.Println(OutputText)
	dynamo := dynamodb.New(sess)

	t := time.Now()
	item := Item{
		TimeStamp:   *aws.String(t.String()),
		InputTextj:  *aws.String(InputText),
		OutputTextj: *aws.String(OutputText),
	}
	av, err := dynamodbattribute.MarshalMap(item)
	if err != nil {
		fmt.Println(err.Error())
	}
	tableName := "translate-history"
	input := &dynamodb.PutItemInput{
		TableName: aws.String(tableName),
		Item:      av,
	}

	_, err = dynamo.PutItem(input)
	if err != nil {
		fmt.Println(err.Error())
	}
}

//Translate/translate.go
package Translate

import (
	"fmt"
	"log"

	"github.com/ahmos0/goLambdaFirst.git/database"
	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/translate"
)

var OutputText string

func TranslateFunc(req events.APIGatewayProxyRequest) string {
	InputText := req.QueryStringParameters["InputText"]
	InputLang := "ja"
	OutputLang := "en"
	sess := session.Must(session.NewSession())
	trans := translate.New(sess)

	log.Println(InputText + "これです")

	result, err := trans.Text(&translate.TextInput{
		SourceLanguageCode: aws.String(InputLang),
		TargetLanguageCode: aws.String(OutputLang),
		Text:               aws.String(InputText),
	})
	if err != nil {
		log.Print("おかしいぜ")
	}

	OutputText := *result.TranslatedText

	fmt.Println(OutputText + "わーい")
	database.OperateDB(InputText, OutputText) //ここでdatabaseに追加する

	return OutputText
}

handler.go は前と同じコードです。

実際にできたものを確認していきます

API Gatewayからアクション>APIのデプロイを選択

このようにします。

GETを選択し、URLの呼び出しというところのリンクに飛びます.

すると今のままでは、Internal server errorが返ってきます。

{"message": "Intenal sever error" }

URLの末尾をdev/translate?InputText=やったか?

のように指定すると

did you do it?

のように返ってきます。

DynamoDBの方には、タイムスタンプ、入力した日本語、返ってきた英語が保存されます。



こんな感じですね。

テストのやり方

テストイベントを作成して、右上のテストからテストを実行します。

成功すれば、この上に成功と緑色で表示されログを見れます。

今回は、queryStringParametersの中身だけ変更しました。そのほかはAPICallのテンプレです。

 

今回のgithub repository

github.com

感想と展望

AWSに触れるのは初めてでさまざまな難題にも悩まされましたがなんとか完走できてよかったです。結構詰まることもあったので、できた時は快感でした!!!一つずつ積み重ねていくことの重要性を実感できました。またpythonで話を聴きながらgoで書くというのが自分の力で実装できたという自信にもつながったのでよかったです。ここまで読んでいただきありがとうございました。拙い部分も多いので、間違った点などあればご指摘いただけると幸いです。次回またよろしくお願いいたします!!!

 

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

 

参考リンク

pages.awscloud.com

aws.amazon.com

github.com

クリーンアーキテクチャについて話を聞いたんです。

はじめに

こんにちは!!会津大学学部二年のHAMADAです。

季節も変わり始め、寒くなってきましたが生きております。

今回は、クリーンアーキテクチャについてのお話です。

動機・目的

10月29日~10月30日に会津大学の大学祭があります。その中で、学内でハッカソンが開催され参加することにしました!!!それに際して、チームのつよつよ先輩方に昨日アーキテクチャのお話をして頂いたのでアウトプットとして書きます。アーキテクチャの学習は初めてで、足りないポイント、間違った解釈があるかと思いますが、暖かい目でみていただけると嬉しいです。

クリーンアーキテクチャとは

層の話
  • Controller層
    外部と内部の変換。一番外側の層。user側に触れるもの。

  • UseCase層
    ドメインロジック(役割のこと)/アプリケーションサービス
  • Service層
    ドメインサービス(仕様上これ以上分割できない操作)
  • Repository層
    外部との接続(APIやDBなどがここに入る)
  • Model層
    ドメインモデル(仕様上決まっているもの。これを変えることはほとんどない。)

UseCase層とRepository層の関係

UseCase層はRepository層にふんわりとした抽象的なものを要求します。"こんな感じのものが欲しいな〜w"みたいな感じ。UseCase層でinterfaceを実装して、Repositoryで具体性を持たせています。

Service層の必要性

Service層は、必要な場合となくても良い場合の両方が存在するようです。Service層が意味をなさない場合でも、実装して繋ぎ役としてわかりやすい関係性にすることもあります。

先輩からのクイズ

Q.Userを取得するエンドポイントをざっくりと実装してください(id指定で関数名だけでOK)

私が最初に、書いたのがこちら。

なんか汚い字で色々書いてますねw

Controller層は、目的となるUserを取得することから、GetUser(id)と命名しています。

UseCase層は、interface的な定義なので、GetUser(id)と命名しましたが、Controller層と同じ命名で良いのか悩んでいます。

Service層では、Userを取得する方法、手段という点からidを使うという発想によって命名しています。命名に不安を覚えています。

Repository層は、Userを取得するという考えから、この命名にしています。idを使って取得することから、Nameだけでいいのかな?情報という意味でinf(information)にすべきか悩んでいます。

Model層は、先輩から与えられました。

添削

Controller層は、OKでした。

UseCase層は、実際の定義となる部分なのでOKでした。

Service層は、微妙でした。意図するところはあっていましたが、命名としてはFindを用います。FindUserByIdとするのが良さそう。

Repository層は、考え方はOK。しかし、NameにするとModelが違うと不適切なので、FindUserByIdとするとよさそう。

 

次のクイズ........

Q.UserNameを取得するエンドポイントをざっくりと実装してください(id指定で関数名だけでOK)

(画像がありませんでした.....すみません)

Controller層は、GetUserName (id)。

UseCase層は、GetUserName (id)。

Service層は、FindUserNameById(id) 。これは、ちょっと悩みました。FindUserNameByIdにするか、FindUserByIdにするか。必要なアクションはNameを得ることなので、FindUserNameById(id) に決定しました。

Repository層は、ここで取得するのはUserなので、FindUserById(id)にしました。

Model層は、与えられました。

添削

全てOKをいただきました!!!!!! やった!!!!!

Service層の悩んだところですが、Nameを得るが最低限必要だということで、問題ありませんでした。

実際の構成

APIを作るときの例

1エンドポイント 1Controller

1エンドポイント 1UseCase

//controller/user.go
//ここで使うのはUserUseCase
type UseControllerImpl struct {
u usecase.UseUseCase
}

ここではControllerがUseCaseに依存しています。

usecase/user.go

ここでは、UserUseCase interfaceを定義。これは、ControllerがUseCaseに依存しています。

次に、UseRepositoryを定義。これは、RepositoryがUseCaseに依存しています。

そして、UserUseCaseImplを実装します。ここでは、普通にメソッドとして、実装します。

コンストラクタも実装する。(今回はgoを使いますが、慣例的にNew~という名前をつけます。)

 

DIPの話

DIPとは、Dependency Inversion Principle。依存性逆転の原則です。

SOLID原則のDです。

今まで、A->BというAの中でBの関数を使うという実装だったとして、B->Aという方向にするには、Aをinterfaceに依存させて、そのinterfaceを継承したBのなかで実装する。ということを行うとできます。

 

感想と展望

今回初めて、アーキテクチャについて学習しました。難しかったですが、かなり面白い概念だなと思いました。実際身につけて使ったり、説明するのはとても大変ですが少しずつでも理解し使えるようになりたいと思います。続きの話を書くこともあるかもしれません。先輩方に感謝を!!!!間違っているところなどの指摘があれば、アドバイスよろしくお願い致します。ここまでお付き合いありがとうございます!!!!

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

UnityでARに入門した話

はじめに

こんにちは!!会津大学学部二年のHAMADAです。

夏休み第二弾のブログです!!八月も終わりに近づいてきましたが、まだまだ頑張ります。

第三弾も頑張ってやりたいですね.....

さて今回は、UnityでARに入門した話です。

 

動機・目的

私は大学でA-PxL(Aizu-Progressive xr Lab)というサークルに所属しています。しかし今までじっくりとxR開発を行なってこなかったので、一念発起してARに取り組むことにしました!!サークル内でも、VR開発がメインなのでこれを機にARを頑張りたい。

 

開発準備

使用したもの
  • Unity
    いつもお世話になりますUnity先生。容量を結構持って行かれて辛い。

  • C#
    iOSのAR開発では、Swiftも使いますがUnityなのでC#
  • Xcode
    Unityからbuildして、Xcode経由でiPhoneにやる。こいつも容量食う。
  • iPhone 12 mini
    私の相棒です。
  • Macbook Air
    頑張ってくれてますが、容量が足りません。Unityはギリ動く。

実装内容について

Unityのセッティング

まず、AR開発ができるように設定していきます。

以下に手順を書いていきたいと思います。

  1. Unityで新しいプロジェクトを作成します。(3Dコアで構いません)
  2. プロジェクトが開かれたら、Edit > ProjectSettings...を開きます。
    XR Plug-in Management > iOSを選択し、ARKitにチェックを入れます。

     

  3. Window > Package Manager を開きます。
    AR Foundation, ARKit XR Pluginをインストールする。

    とりあえずはこれでOKです。

具体的に実装してみる

今回した実装は、水平面を検知してその場所を示してくれるものと画面 (iPhoneの画面)をタップするとCubeが配置されるというもの。

順番に手順を示します。

まず、水平面の検知

  1. Hierarchy上で右クリックして、XR > AR Session Origin, XR > AR Sessionを追加します。Main cameraは必要ないので消去します。

  2. AR Session OriginのInspectorからAdd Component > AR Plane Managerを追加します。

  3. Hierarchy上で右クリックして、XR > AR Default Planeを追加します。このAR Default PlaneをPrefab化して、Hierarchy上からは消去しておきます。

  4. AR Session Origin のInspectorで、AR Plane Manager > Plane PrefabにPrefab化したAR Default Planeを追加する。

  5. AR Plane Manager > Detection ModeはHorizontalにしておく。

 

次に、タップするとCubeが配置されるようにします。

  1. AR Session OriginのInspectorからAdd Component > AR Raycast Managerを追加します。

  2. Hierarchy上で右クリックして、3D Object > Cubeを追加します。このCubeはPrefab化して、Hierarchy上からは消去します。

  3. Project上で右クリックして、Create > C# Scriptから画面をタップするとCubeが配置されるようにするためのスクリプトを作成します。
    コードは以下のようなものです。
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.XR.ARFoundation;
    using UnityEngine.XR.ARSubsystems;
    public class tap_object : MonoBehaviour
    {
        [SerializeField] GameObject cube;
        private ARRaycastManager m_RaycastManager;
    
        List m_Hits = new List();
    
        private void Awake()
        {
            m_RaycastManager = GetComponent();
        }
        void Update()
        {
            if (Input.touchCount > 0)
            {
                Vector2 Touch_Position = Input.GetTouch(0).position;
                if (m_RaycastManager.Raycast(Touch_Position, m_Hits, TrackableType.Planes))
                {
                    Instantiate(cube, m_Hits[0].pose.position, Quaternion.identity);
                }
            }
        }
    }
  4. AR Session OriginのInspectorからAdd Component > 作成したC# Scriptを追加します。追加したC# Script > Cubeに先ほどPrefab化したCubeを追加します
  5. これで完成です!!

Inspectorがどんな感じか添付しておきます。

ビルドする

具体的に実装してみる

まずは、Unity側からBuildします。

File > Build Settingsを開いて以下の画像のように設定します。



ここで左下の Player Settingsを開きます。

Requires ARKit supportにチェックを入れます。

その後Build SettingsからBuildします。XcodeとARKitのバージョンの不一致からBuildできない旨の警告が出た場合は、Player Settings > Target minimum iOS Versionを入力し直すと上手くいきました。

次にXcodeを起動します。

Open a project or fileから、先ほどbuildしたものの中にある、拡張子がxcodeprojのものを選択します。

Projectが開けたら、Xcodeでbuild and run していきますが、このままではBuildに失敗します。

buildしてみるとissueが吐かれるので、issueをクリックしてみると次のような画面になりました。

ここでAutomatically manage siginingにチェックを入れます。

Teamの欄は、画像上ではNoneにしていますが、ここで自分のTeamを作成するか、既にあるものを選択します。

その後、iOSの部分の表示が変わります。それでもダメそうであれば、Bundle Idenntifierの部分を画像のように、末尾に現在の日付を入れるなどします。その後、iOSの部分のTry Againを押すと上手くいきます。

Bundle Identifierは同じものが存在するとうまくいかないようです。

 

MaciPhoneを繋ぐとXcode上部にUnity-iPhone > "自分のiPhoneの名前"のように表示されます。

いよいよbuildします。

buildするとiPhone側で"信頼されていないデベロッパ"うんぬんかんぬんと出ます。

Mac側ではこんな感じのが出ます。

                                    

iPhoneの設定 > 一般 > VPNとデバイス管理 を選択します。

その中に、デベロッパAPPの欄があるのでそれを選択して、"信頼する"と選択するとbuildできます。

そうすると、アプリがiPhoneに表示されて開くことができるようになります。

                                             

 

デモ

実際に動かしてみるとこんな感じ。

                                        

おもしろい!!!

黒枠で囲まれて、黄色くなっているのが平面を検知してくれているものです。

タップするとCubeが置かれていますが、タップを続けると連続してCubeが置かれてしまうので改良の余地ありです。

 

感想と展望

今回はARに入門してみるということで取り組みました。現実に+αされるのがすごく面白くて楽しかったです。Unityの使い方自体は、他の開発と大きく変わるものではなかったのでそこまで大変ではなかったように思います。Unityの公式ドキュメントを主に使用しましたが、英語力の衰えを感じたので、英語を頑張る必要がありそうですw Unityでのゲーム開発よりは情報が少なく、その点は難しかったように思います。ここまでお付き合いありがとうございました!

 

参考リンク

docs.unity3d.com

ticklecode.com

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub

LINEからのメッセージでラズベリーパイを使ったLチカをした話

 

はじめに

みなさまお元気ですか?会津大学学部二年のHAMADAです。

夏休みに入ったので色々あげていきたいという決心から第一弾のブログです!!

第二弾はあるのでしょうか.......

さて今回は、LINEからのメッセージでラズベリーパイを使ったLチカをした話を書いていきます。

 

動機・目的

後々、お風呂が溜まったことを知らせてくれるシステムを作ろうと考えているのですが、その足掛かりとして今回ラズパイで簡単なものを実装しようと考えました。今回は、少し長い内容ですがお付き合いいただけると幸いです。

 

開発準備

使用したもの
  • Raspberry Pi 4 Model B 4GB
    現在半導体不足で高い。昨年の夏買っていたので損はしていない。

  • Node.js
    Node.jsでLチカもやりました。line-bot-sdkもあるのでgood!!
  • ngrok
    ローカルサーバーを公開できるやつ。
  • LINE Messaging API
    LINE Bot開発に使いました。
  • SDカード(32GB)
    32GBは最低限必要なようです。

  • Visual Studio Code
    SSHをして、vscodeスクリプトを書きます。

  •  電子工作のパーツ
    • ジャンパー線
    • LED
    • 330Ω抵抗

    キットを購入しました。こういったパーツって見てるだけでワクワクしますよね?

 

 

実装内容について

ラズベリーパイのセッティング

ラズパイを触るのが初めてだったので、OSのインストールから始めました。

以下に手順を書いていきたいと思います。

  1. Raspberry Pi imagerをダウンロードして、起動する。 
    ダウンロードリンク->Raspberry Pi OS – Raspberry Pi

    Raspberry Pi imager 画面


  2. OSを設定すると、上の画面のように歯車の設定が出てくる。そこを開いて以下のように設定する。現在は、SSHの設定も楽にできるようになっているようです。

    設定前半

    設定後半

    セキュリティ的に一部設定は空欄にしていますが、赤くチェックされているところを設定してください。ロケール設定についても必要であれば設定しましょう。

  3. 次にラズベリーパイに差し込むSDカードを使っているPCに差し込んで、OSをインストールします。これが終わればOSの設定は終了です。

  4. 次にssh接続をします。ターミナルから打っても良いのですが、vscodeで設定して接続しました。
    vscodeのリモートエクスプローラーから新しくsshの設定をします。+を押すとできます。拡張機能がない方は、vscode内でRemote-sshというものをいうものをインストールしましょう。ssh接続コマンドを入力するように求められるので以下のように入力します。
    ssh {usrname}@hostname
    //今回は ssh pi@raspberrypi

    ~/.ssh/configに以下のように記載する

    Host raspberrypi.local
    HostName raspberrypi.local
    User pi
    これでssh接続ができるようになりました。

  5. ラズパイ内でnodeをインストールする。
  6. sudo apt-get install -y nodejs npm
    sudo npm cache clean
    sudo npm install -g n
    sudo n stable
    sudo apt-get purge -y nodejs npm
    //パスが通っているか確認する
    printenv PATH
    node -v
    //これで出たバージョンを次の{node -v}に記入
    export PATH="$PATH:/usr/local/n/versions/node/{node -v}"

 

とりあえず一旦Lチカしてみる

今回はNode.jsでLチカしてみました。(C言語Pythonでも一応できました)

コードは以下のようになります。

let gpio = require('rpi-gpio');

 const LED_PIN = 40;
 let ledOn = true;

 gpio.setup(LED_PIN, gpio.DIR_OUT, () => {
     setInterval(() => {
         if (ledOn) {
             gpio.write(LED_PIN, false);
             ledOn = false;
             console.log("fugafuga");
         }
         else {
             gpio.write(LED_PIN, true);
             ledOn = true;
             console.log("hogehoge");
         }
     }, 500);
 });

0.5秒ごとにLEDがチカチカするようなコードになっています。

GNDには6番のピンを入力には40番のピンを使用しました。

 

pinout

とコマンドを打つとピンを確認できます

他にもラズパイの基盤の配置なども表示されます。

 

簡単にメソッドの説明(のちに使うものも紹介しておきます)

rpi-gpioをインストールして利用できるものを使う。

setupメソッドはsetup(channel,[direction, edge], callback)というもの。

writeメソッドはwrite(channel, value, [callback])というもの。

destroyメソッドはdestroy()で今までのピンの設定を破棄してくれるもの。

 

~引数の説明~

channel・・・使うピンを決める。

direction・・・ピンの向きを決める。

edge・・・割り込みの状態を決める。

callback・・・ エラーが発生した場合、第一引数にErrorを指定する。

value・・・on、offを決める。

 

回路

こんな感じで繋ぎました。

                              

デモといきたいところですが実際に光るのは、LINEとの連携の際にお見せします。

LINEBotとの連携

簡単に手順も含めて、説明していこうと思います。(以下のコマンドやファイル作成は全てSSH先のラズパイ上で行います)

まずは、LINEとngrokの設定

  1. まずはLINE Developersにログインして各種設定を行います。
  2. チャンネルを作り、チャネルアクセストークンとチャネルシークレットを入手します。
  3. ngrokにログインしてYour AuthTokenを手に入れる。そして以下のコマンドを実行する。
    npx ngrok authtoken  ここにngrokのYourAuthTokenを貼る
  4. .envを作成して次のように記載する
  5. channelSecret=       //ここにチャネルシークレットを貼る
    channelAccessToken= //ここにチャネルアクセストークンを

 

次にサーバーをたてます。

  1. npm startを実行する。
  2. npx ngrok http ポート番号を実行する。これはnpm startとは別のターミナルで実行する。
  3. npx ngrok http ポート番号で開かれたForwardingのhttpsの方をコピーする。
  4. これをLINE Developersのチャンネルのwebhook URLに、3でコピーURLの後ろに/webhookをつけて貼り付ける。
  5. webhookの利用をオンにする。(これを忘れて何度か失敗しました)

LINEのメッセージからLチカをする

仕様としては、メッセージで"Lチカ"と送るとLチカをしてくれて、 "やめる"と送るとLチカを止めてくれるというもの。

server.jsを中心としてプログラムが実行されていくのですが、その辺りはgithubに公開するので、今回のトピックであるLチカのコード(blink.js)を以下に貼ります。

import gpio from 'rpi-gpio';

const LED_PIN = 40;
let ledOn = true;
let stopflag = false;
export const ledfunc = function (stopflag) {
    if (stopflag === false) {
        gpio.setup(LED_PIN, gpio.DIR_OUT, () => {
            setInterval(() => {
                if (ledOn) {
                    gpio.write(LED_PIN, false);
                    ledOn = false;
                }
                else {
                    gpio.write(LED_PIN, true);
                    ledOn = true;
                }
            }, 500);
        });
    }
    else if (stopflag === true) {
        gpio.setup(LED_PIN, gpio.DIR_OUT, pause);
    };
};

function pause() {
    setTimeout(closePins, 500);
}

function closePins() {
    gpio.destroy(function () {
        console.log('All pins unexported');
    });
}

stopflagによってledfuncが呼び出された時に行う処理を変えています。
これは"Lチカ"と"やめる"のメッセージによって分けています。

blink.jsはLINEからのメッセージで処理を決めるtext.jsから呼び出されて実行されます。

 

デモ

いよいよLINEからメッセージを送るとLチカを実践します!!

繋げて撮るのが上手くできなかったので、分けていますがご容赦ください。

あとgifにしたら荒くなってしまいました。一個目が"Lチカ"で二個目が"やめる"です。

できた!!やったーー!!!

感想と展望

やる前はLINEBotとラズパイの連携はめちゃくちゃ難しいものだと思っていましたが、実際やってみると案外なんとかなって嬉しいです。電子部品などを触るのは、ワクワクするので夏休みの工作っぽくて非常に楽しかったです。実は、"やめる"の処理は誤魔化していて、再度"Lチカ"とメッセージを送ると、処理が重なってしまいチカチカが早くなってしまうのでこれをなんとかできるようにしたいと思います。お風呂の水位メーターシステムに一歩近づきました!!何かの参考になれば幸いです。ここまでお付き合い頂きありがとうございました!

 

参考リンク

qiita.com

www.npmjs.com

qiita.com

所属サークル

A-PxL (@aizu_PxL) / Twitter

私のTwitter

HAMADA (@AHMOS_HMD) / Twitter

私のgithub

ahmos0 (HAMADA) · GitHub