シェルスクリプトマガジン

test

第9回 グローバルIPアドレスを通知する

投稿日:2019.07.30 | カテゴリー: 記事

 自宅のネットワークにリモートからアクセスしたい人もいるでしょう。その場合、自宅ネットワークの入り口に配置しているルーターに割り振られた「グローバルIPアドレス」を知らなくてはいけません。

 グローバルIPアドレスは、自宅内から次のコマンドを実行すれば確認できます。

$ curl ifconfig.io

 表示されたIPアドレスでアクセスすればよいのですが、常に同じIPアドレスとは限りません。通常、このIPアドレスは動的に変更されます。

 「ダイナミックDNS」というサービスを利用すれば、動的にIPアドレスが変わっても自宅ネットワークへ「ドメイン名」(ドメイン付きホスト名)でアクセスできます。ダイナミックDNSに関しては、Web連載「UbuntuではじめるLinuxサーバー」の「第10回 インターネットに公開する」を参照してください。ダイナミックDNSを使えば解決しますが、作業が面倒です。また、サーバーをインターネットに公開しているわけではないので特定のドメイン名を付ける必要はありません。

 そこで、ダイナミックDNSを使わずに自宅内からグローバルIPアドレスを監視して更新されたら通知するシェルスクリプトを作成します(図1)。

図1 グローバルIPアドレスを通知する

Vol.61

投稿日:2019.07.25 | カテゴリー: バックナンバー

 約5年ぶりに企業向け有償Linuxディストリビューションのメジャーアップデート版「Red Hat Enterprise Linux 8」(RHEL8)が2019年5月リリースされました。RHELは、サーバーOSとして小規模なシステムから基幹系のような大規模なシステムまで、幅広く利用されています。
 特集1では、このRHEL8に関する概要や特徴、旧版利用者への注意点、情報入手方法をまとめました。開発元のレッドハットの技術者が自ら執筆していますので、RHELユーザーなら必ず読むべき特集記事です。
 2020年にプログラミング教育が小学校で必修となります。「何をすべきか分からない」と感じている人も多いようです。そのような人にお薦めなのが、教育向けシングルボードコンピュータ「BBC micro:bit」です。
 特集2では、このBBC micro:bitを利用したプログラミングとサンプルを紹介しています。ブロックを組み合わせるだけでプログラムが書けるので、プログラムが苦手な人、初めてのプログラミングする人にピッタリです。
 このほか、特別企画では700円くらいで買えるGPSモジュールの電子基板をパソコンから制御します。また、データベース管理システム「MySQL」の新クライアント「MySQL Shell」の入門連載を開始しました。
 今回も読み応え十分のシェルスクリプトマガジン Vol.61。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.61 補足情報

投稿日:2019.07.25 | カテゴリー: コード

漢のUNIX

p.92の1行目にある「透過」は「等価」の誤りです。お詫びして訂正いたします。

情報は随時更新致します。

Webアプリケーションの正しい作り方(Vol.61記載)

投稿日:2019.07.25 | カテゴリー: コード

著者:しょっさん

ソフトウエアを正しく作るために、エンジニアたちはどんなことを知らなければならない でしょうか。実際のコードを使って、より良くしていくためのステップを考えてみましょう。第2回は、プロジェクトを開始するまでの準備を解説します。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図1 注釈を入れたプログラム

# Node.js(Javascript)でプログラムは書かれている
# Expressフレームワークを利用している
const express = require('express');
const app = express()

# SequelizeフレームワークでModelを利用している
const models = require('./models');
const expenses = models.expense;

# POSTデータをパーシングするためのライブラリを利用している
const bodyParser = require('body-parser');

# セッション管理にCOOKIEを利用しているが、特にステートは保管していない
const cookieParser = require('cookie-parser');
const session = require('express-session');
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
  secret: 'secret',
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 24 * 30 * 60 * 1000
  }
}));

# ユーザー情報をプログラムに埋め込んでいる
const users = {
  'user01': 'p@ssw0rd',
  'user02': 'ewiojfsad'
};

# /loginへアクセスするとログイン機能を利用できる (HTMLがべた書き)
app.get('/login', (req, res) => {
  res.send('<h1>LOGIN</h1><form action="/login" method="post">ユーザーID:<input type="text" name="user" size="40"><br />パスワード<input type="password" name="password"><input type="submit" value="ログイン">');
});
# ログインに成功しても、失敗しても、画面上は特に何も起きない
app.post('/login', (req, res) => {
  if (eval("users." + req.body.user) === req.body.password) {
    req.session.user = req.body.user;
  }
  res.redirect('/');
});

# /expenseへPOSTすると経費を登録できるが、特にエラー処理はない
app.post('/expense', (req, res) => {
  expenses.create(req.body)
    .then(() => {
      res.redirect('/');
    });
});

# /へアクセスすると誰でも経費の一覧が見られる。ログイン・経費入力フォームへのリンクを表示 (HTMLがべた書き)
app.get('/', (req, res) => {
  const user = req.session.user || '名無しの権兵衛';
  res.writeHead(200, { "Content-Type": "text/html" });
  res.write(<h1>Hello ${user}</h1><table><tr><th>ID</th><th>申請者名</th><th>日付</th><th>経費タイプ</th><th>経費詳細</th><th>金額</th></tr>);
  expenses.findAll()
    .then(results => {
      for (let i in results) {
        res.write(<tr><td>${results[i].id}</td><td>${results[i].user_name}</td><td>${results[i].date}</td><td>${results[i].type}</td><td>${results[i].description}</td><td>${results[i].amount}</td></tr>);
      }
      res.write('</table><a href="/login">ログイン</a><a href="/submit">経費入力</a>');
      res.end();
    });
});

# /submitへアクセスすると、経費入力フォームが表示される (HTMLがべた書き)
app.get('/submit', (req, res) => {
  const user = req.session.user || '名無しの権兵衛';
  res.send(<h2>経費入力</h2><form action="/expense" method="post">申請者名:<input type="text" name="user_name" value="${user}"><br />日付:<input type="date" name="date"><br />経費タイプ:<input type="text" name="type"><br />経費詳細:<input type="text" name="description"><br />金額:<input type="number" name="amount"><br /><input type="submit" value="経費申請">);
});

# Webアプリケーションサーバーとして起動する
const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(http://localhost:${port});
})

香川大学SLPからお届け!(Vol.61掲載)

投稿日:2019.07.25 | カテゴリー: コード

著者:宇野 光純

 今回は、Windowsアプリケーションとして動く簡単な2Dゲームの開発を紹介します。汎用プログラミング言語の「C++」と、オープンソースのパソコンゲーム開発用ライブラリの「DXライブラリ」を組み合わせることで、時間と労力は必要ですが、Unityなどのゲームエンジンよりも自由度の高いゲーム開発ができます。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図1 DXライブラリを使用する際の基本コード(Main.cpp)

#include "DxLib.h"
// プログラムはWinMainから開始
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{
  // DXライブラリ初期化処理
  if( DxLib_Init() == -1 ) {
    return -1; // エラーが起きたら直ちに終了
  }
  // ウィンドウモードで起動、拡大して表示する
  ChangeWindowMode(TRUE);
  SetWindowSizeExtendRate(1.5);
  // ちらつきを消すために、描画先を裏画面にする
  SetDrawScreen(DX_SCREEN_BACK);
  while (ProcessMessage() == 0 && ScreenFlip() == 0 &&
         ClearDrawScreen() == 0) {
    // ここにゲームの処理を書く
  }
  DxLib_End(); // DXライブラリ使用の終了処理
  return 0; // ソフトの終了
}

図2 「Info.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Info.h"
int g_Width, g_Height; // ウィンドウの横幅と縦幅
int g_StageTime; // ステージ開始からの経過時間
static const int KEY_NUM = 256; // キー配列の最大値
char g_Buf[KEY_NUM]; // キーの状態を保持する配列

void InfoInitialize() { // 各データの初期化
  GetScreenState(&g_Width, &g_Height, NULL);
  g_StageTime = 0;
}
void InfoUpdate() { // 各データの更新
  g_StageTime++;
}
int GetWidth() { // ウィンドウの横幅を返す
  return g_Width;
}
int GetHeight() { // ウィンドウの縦幅を返す
  return g_Height;
}
int GetStageTime() { // ステージ開始からの経過時間を得る
  return g_StageTime;
}
void KeyUpdate() { // 全キーの状態を更新する
  GetHitKeyStateAll(g_Buf);
}
bool GetKey(int key_code) { // 指定したキーの状態を取得する
  if (g_Buf[key_code]) { return true; }
  return false;
}

図3 「Info.h」ファイルに記述するコード

#pragma once

void InfoInitialize();
void InfoUpdate();
int GetWidth();
int GetHeight();
int GetStageTime();
void KeyUpdate();
bool GetKey(int key_input);

図4 「Object.h」ファイルに記述するコード

#pragma once
class Object {
protected:
  int size;                // 画像サイズ
public:
  double x, y;             // X座標、Y座標
  virtual void Update() {} // 更新
  virtual void Draw() {}   // 描画
};

図5 「Player.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Player.h"
#include "Info.h"
#include <math.h>

int Player::image;
Player::Player() {
  x = y = 200.0; v = 4;    // 座標と速度の初期化
  SetImage();              // 画像関連の設定
}
void Player::Update() {
  Move();                 // 移動の更新
}
void Player::Draw() {      // 描画
  DrawGraph((int)(x - size / 2), (int)(y - size / 2), image, TRUE);
}
void Player::SetImage() {  // 画像関連の設定
  size = 64; image = LoadGraph("./images/player.png");
}
void Player::Move() {      // 自機操作
  double nxv = 0, nyv = 0; // 移動量保持用
  // X方向の移動量の調整
  if (GetKey(KEY_INPUT_LEFT)) { nxv -= v; }
  if (GetKey(KEY_INPUT_RIGHT)) { nxv += v; }
  // Y方向の移動量の調整
  if (GetKey(KEY_INPUT_UP)) { nyv -= v; }
  if (GetKey(KEY_INPUT_DOWN)) { nyv += v; }
  if (GetKey(KEY_INPUT_LSHIFT)) { nxv /= 2; nyv /= 2; }
  if (nxv != 0 && nyv != 0) { nxv /= sqrt(2); nyv /= sqrt(2); }
  x += nxv; y += nyv; // 移動量の加算
 // 移動範囲外に出たときは範囲内に戻す
  if (y < 0) { y = 0; }
  if (GetHeight() < y) { y = GetHeight(); }
  if (GetWidth() < x) { x = GetWidth(); }
  if (x < 0) { x = 0; }
}

図6 「Player.h」ファイルに記述するコード

#pragma once
#include "Object.h"

class Player : public Object {
private:
  static int image; // 画像ハンドル
  void SetImage();  // 画像関連の設定
  void Move();      // 自機の操作
public:
  double v;         // 移動速度
  Player();
  void Update();    // 更新
  void Draw();      // 描画
};

図7 「Enemy.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "Enemy.h"
#include "Info.h"

int Enemy::image;
Enemy::Enemy() {
  x = GetWidth() / 2; y = 50.0; // X、Y座標の初期化
  xv = yv = 0.0;                // 増加量の初期化
  this->SetImage();             // 画像関連の設定
}
void Enemy::Update() {
  x += xv; y += yv;
}
void Enemy::Draw() {
  DrawGraph((int)(x - size / 2),
            (int)(y - size / 2), image, TRUE);
}
void Enemy::SetImage() {
  size = 64;
  image = LoadGraph("./images/enemy.png");
}

図8 「Enemy.h」ファイルに記述するコード

#pragma once
#include "Object.h"

class Enemy : public Object {
private:
  static int image; // 画像ハンドル
  void SetImage();  // 画像関連の設定
public:
  double xv, yv;    // X、Y 方向の増加量
  Enemy();
  void Update();    // 更新
  void Draw();      // 描画
};

図9 「MainScene.cpp」ファイルに記述するコード

#include "DxLib.h"
#include "MainScene.h"
#include "Info.h"

MainScene::MainScene() {
  // 背景を灰色に
  SetBackgroundColor(100, 100, 100);
  InfoInitialize(); // ウィンドウサイズなどの初期化
  player = Player(); // 自機の初期化
  enemy = Enemy(); // 敵機の初期化
}
void MainScene::Update() {
  InfoUpdate(); // 各データの更新
  player.Update(); // 自機の操作
  enemy.Update(); // 敵機の更新
}
void MainScene::Draw() {
  player.Draw(); enemy.Draw(); // 自機と敵機の描画
  // 経過時間の描画
  DrawFormatString(GetWidth() / 2 - 40, 0,
                   GetColor(255, 255, 255),
                   "Time : %d", GetStageTime());
}

図10 「MainScene.h」ファイルに 記述するコード

#pragma once
#include "Player.h"
#include "Enemy.h"

class MainScene {
private:
  Player player;
  Enemy enemy;
public:
  MainScene();
  void Update(); // 更新
  void Draw();   // 描画
};

図11 コードを追加した「Main.cpp」ファイルの内容

#include "DxLib.h"
#include "MainScene.h"
#include "Info.h"

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow )
{
  if( DxLib_Init() == -1 ) {
    return -1;
  }
  ChangeWindowMode(TRUE);
  SetWindowSizeExtendRate(1.5);
  SetDrawScreen(DX_SCREEN_BACK);
  // シーンの初期化
  MainScene ms = MainScene();
  while (ProcessMessage() == 0 && ScreenFlip() == 0 &&
         ClearDrawScreen() == 0) {
    KeyUpdate();
    // ゲーム終了
    if (GetKey(KEY_INPUT_ESCAPE)) { break;}
    ms.Update(); // 更新
    ms.Draw();   // 描画
  }
  DxLib_End();
  return 0;
}

機械学習のココロ(Vol.61掲載)

投稿日:2019.07.25 | カテゴリー: コード

著者:石井 一夫

今回はディープラーニングのバリエーションとして、画像認識によく用いられる「CNN」(Convolutional Neural Network)と、自然言語処理によく用いられる「RNN」(Recurrent Neural Network)について紹介します。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図3 サンプルコードのCNN 定義部分(抜粋)

def cnn_model_fn(features, labels, mode):
    """Model function for CNN."""
    # Input Layer
    input_layer = tf.reshape(features["x"], [-1, 28, 28, 1])

    # Convolutional Layer #1
    conv1 = tf.layers.conv2d(
        inputs=input_layer,
        filters=32,
        kernel_size=[5, 5],
        padding="same",
        activation=tf.nn.relu)

    # Pooling Layer #1
    pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2)

    # Convolutional Layer #2 and Pooling Layer #2
    conv2 = tf.layers.conv2d(
        inputs=pool1,
        filters=64,
        kernel_size=[5, 5],
        padding="same",
        activation=tf.nn.relu)
    pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2)

    # Dense Layer
    pool2_flat = tf.reshape(pool2, [-1, 7 * 7 * 64])
    dense = tf.layers.dense(inputs=pool2_flat, units=1024, 
    activation=tf.nn.relu)
    dropout = tf.layers.dropout(
    inputs=dense, rate=0.4, training=mode == tf.estimator.ModeKeys.TRAIN)

    # Logits Layer
    logits = tf.layers.dense(inputs=dropout, units=10)
(略)

図7 サンプルコードのRNN 定義部分

def build_model(vocab_size, embedding_dim, rnn_units, batch_size):
    model = tf.keras.Sequential([
         tf.keras.layers.Embedding(vocab_size, embedding_dim,
                                   batch_input_shape=[batch_size, None]),
         rnn(rnn_units,
                  return_sequences=True,
                  recurrent_initializer='glorot_uniform',
                  stateful=True),
         tf.keras.layers.Dense(vocab_size)
     ])
     return model

シェルスクリプトマガジンvol.61 Web掲載記事まとめ

投稿日:2019.07.25 | カテゴリー: コード

004 レポート 新版の「Debian 10」を公開
005 レポート 新開発の汎用メモリーアロケータ
006 NEWS FLASH
008 特集1 Red Hat Enterprise Linux 8/森若和雄
018 特集2 micro:bitを動かそう/中田和宏
030 特別企画 GPSモジュールで遊ぼう/麻生二郎 コード掲載
037 姐のNOGYO
038 ラズパイセンサーボードで学ぶ 電子回路の制御/米田聡 コード掲載
041 Webアプリケーションの正しい作り方/しょっさん コード掲載
052 仮想現実/桑原滝弥・イケヤシロウ
054 MySQL Shellを使おう/梶山隆輔
062 中小企業手作りIT化奮戦記/菅雄一
068 バーティカルバーの極意/飯尾淳 コード掲載
074 香川大学SLPからお届け!/宇野光純 コード掲載
080 円滑コミュニケーションが世界を救う!/濱口誠一
082 機械学習のココロ/石井一夫 コード掲載
086 法林浩之のFIGHTING TALKS/法林浩之
088 漢のUNIX/後藤大地
094 ユニケージ新コードレビュー/坂東勝也
102 Techパズル/gori.sh
104 コラム「近未来に起こってほしいこと」/シェル魔人

特別企画 GPSモジュールで遊ぼう(Vol.61掲載)

投稿日:2019.07.25 | カテゴリー: コード

著者:麻生 二郎

「NEO-6M」というGPS(地理情報システム)モジュールを搭載したアンテナ付き基板が数百円に 購入できます。この基板にUART-USB変換ケーブルを接続するだけで、パソコンから位置の情報を取得できます。最新のLinuxディストリビューションとシェルスクリプトで遊んでみましょう。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図13 GPSモジュールを利用するシェルスクリプト(gps_data.sh)

#!/bin/sh

## Yahoo! JAPANのアプリケーションID
yahoo_appid="アプリケーションID"

## 地図の大きさ、縮尺
width="640"
height="480"
scale="17"

## GPSのデータ取得
timeout 3 cat /dev/ttyUSB0 > /tmp/gps.log

##緯度計算
latitude_raw=$(cat /tmp/gps.log | grep GPGLL | head -1 | cut -f 2 -d ",")
latitude_ns=$(cat /tmp/gps.log | grep GPGLL | head -1 | cut -f 3 -d ",")
ns=1;test "${latitude_ns}" = "S" && ns=-1
latitude_do=$(echo "scale=0;${latitude_raw} / 100" | bc)
latitude=$(echo "scale=5;${ns} * ((${latitude_raw} - ${latitude_do} * 100) / 60 + ${latitude_do})" | bc)

##経度計算
longitude_raw=$(cat /tmp/gps.log | grep GPGLL | head -1 | cut -f 4 -d ",")
longitude_ew=$(cat /tmp/gps.log | grep GPGLL | head -1 | cut -f 5 -d ",")
ew=1;test "${longitude_ew}" = "W" && ew=-1
longitude_do=$(echo "scale=0;${longitude_raw} / 100" | bc)
longitude=$(echo "scale=5;${ew} * ((${longitude_raw} - ${longitude_do} * 100) / 60 + ${longitude_do})" | bc)

##HTMLファイル生成
echo "<!DOCTYPE html>" > gps.html
echo "<html lang='ja'>" >> gps.html
echo "<head>" >> gps.html
echo "<meta charset='UTF-8'>" >> gps.html
echo "<title>場所</title>" >> gps.html
echo "</head>" >> gps.html
echo "<body>" >> gps.html
echo "<p>現在地<br>" >> gps.html
echo "<img width=${width} height=${height} src='https://map.yahooapis.jp/map/V1/static?appid=${yahoo_appid}&lat=${latitude}&lon=${longitude}&z=${scale}&width=${width}&height=${height}&pointer=on'>" >> gps.html
echo "</p>" >> gps.html
echo "</body>" >> gps.html
echo "</html>" >> gps.html

バーティカルバーの極意(Vol.61掲載)

投稿日:2019.07.25 | カテゴリー: コード

著者:飯尾 淳

 前回から、「GDHP」(Gniibe Distributed HanoiProtocol)というプロトコルでハノイの塔パズルを解き、それを可視化して確認しようという試みに挑戦しています。プログラムはJavaScript で記述し、「p5.js」というグラフィックスライブラリを利用します。
 前回は、初期状態の塔を積み上げるところまで完成させました。今回はアニメーションで実際に動作させ、GDHPでパズルが解けることを確認しましょう。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図1 向かい合う2本の塔を点滅させるコード

(略)
const POSITIONS = { 'Source'      : [0.268, 0.714],
                    'Auxiliary'   : [0.500, 0.286],
                    'Destination' : [0.732, 0.714] };
const FLASH_CTR = 20;

class Position {
(略)
}

class Tower {
  constructor(name, disks, direction=null) {
(略)
    this.pos = new Position(rx*C_WIDTH, ry*C_HEIGHT);

    this.exchanging = false;
    this.flash_ctr = 0;
  }

  draw() {
(略)
    // 支柱を描く
    stroke('brown');
    fill(this.exchanging & (this.flash_ctr < FLASH_CTR/2) 
      ? 'gold' : 'white');
    ellipse(pos.x, pos.y, 2*POLE_R);

(略)
    line(sx, sy, dx, dy);
  }

  flash_pole() {
    this.exchanging = (this.direction.direction === this);
    this.flash_ctr++;
    this.flash_ctr %= FLASH_CTR;
  }
}
)
function draw() {
  background('beige');
  [src, aux, dst].forEach(function(t) { 
    t.draw(); 
    t.flash_pole();
  })
}

図3 draw()関数を修正する

var moving_disk = null;
(略)
function draw() {
  background('beige');
  [src, aux, dst].forEach(function(t) {
    t.draw();
    t.flash_pole();
  })

  if (moving_disk == null) {
    moving_disk = pop_disk();
  } else {
    var finished_p = draw_moving_disk();
    if (finished_p) {
      turn();
      moving_disk = null;
    }
  }
}

図4 円盤を移動させる修正

(略)
const FLASH_CTR = 20;
const STEPS = 30;

class Vector {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

class Position extends Vector {
  constructor(x, y) { super(x, y); }

  move(vec) {
    this.x += vec.x;
    this.y += vec.y;
  }
}  

class Disk {
  constructor(level) {
  this.level = level;
  this.color = COLORS[level];
  this.r = (DISK_R-POLE_R)*(N_DISKS-level)
              / N_DISKS + POLE_R;
}

  draw(pos) {
    stroke('black');
    fill(this.color);
    ellipse(pos.x, pos.y, 2*this.r);
  }
}

class MovingDisk extends Disk {
  constructor(level, from, to) {
    super(level); 
    this.pos = new Position(from.pos.x, from.pos.y);
    this.vec = new Vector((to.pos.x-from.pos.x)/STEPS, 
                          (to.pos.y-from.pos.y)/STEPS);
    this.move_ctr = 0;
    this.from = from;
    this.to = to;
  }

  step_forward() {
    this.pos.move(this.vec);
    this.move_ctr++;
  }

  finish_p() {
    var ret_flag = false;
    if (ret_flag = (this.move_ctr == STEPS)) {
      this.to.disks.push(new Disk(this.level));
    }
    return ret_flag;
  }
}

class Tower {
(略)
  flash_pole() {
    this.exchanging = (this.direction.direction === this);
    this.flash_ctr++;
    this.flash_ctr %= FLASH_CTR;
  }

  get toplevel() {
    var l = this.disks.length;
    // '-1'は円盤が一つもないことを示す
    return (l > 0) ? this.disks[l-1].level : -1;
  }
}

var src = new Tower('Source', N_DISKS);
(略)
src.direction = (N_DISKS % 2 == 1) ? dst : aux;

// 移動中の円盤
var moving_disk = null;

function pop_disk() {
  var towers = [src, aux, dst].filter(t => t.exchanging);
  var idx, from, to;
  idx = (towers[0].toplevel > towers[1].toplevel) ? 0 : 1;
  [from, to] = [towers[idx], towers[1-idx]];
  return new MovingDisk(from.disks.pop().level, from, to);
}

function draw_moving_disk() {
  var d = moving_disk;
  d.step_forward();
  d.draw(d.pos);
  return d.finish_p();
}

function turn() {
  // まだ何もしない
}

function setup() {
  createCanvas(C_WIDTH, C_HEIGHT);
  frameRate(30);
}

function draw() {
  background('beige');
  [src, aux, dst].forEach(function(t) { 
    t.draw(); 
    t.flash_pole();
  })

  if (moving_disk == null) {
    moving_disk = pop_disk();
  } else {
    var finished_p = draw_moving_disk();
    if (finished_p) {
      turn();
      moving_disk = null;
    }
  }
}

図6 trun()関数

function turn() {
  [moving_disk.from, moving_disk.to].forEach(function(t) {
    t.direction = ([src, aux, dst]
      .filter(x => (x !== t) && (x !== t.direction)))[0];
    t.exchanging = false;
  })
}

図7 終了条件を追加

(略)
function pop_disk() {
  var towers = [src, aux, dst].filter(t => t.exchanging);
  var idx, from, to;
  if (towers[0].toplevel == towers[1].toplevel) { 
    noLoop(); 
    return null; 
  };
  idx = (towers[0].toplevel > towers[1].toplevel) ? 0 : 1;
  [from, to] = [towers[idx], towers[1-idx]];
  return new MovingDisk(from.disks.pop().level, from, to);
}
(略)

センサーボードで学ぶ電子回路の制御(Vol.61掲載)

投稿日:2019.07.25 | カテゴリー: コード

著者:米田 聡

シルスクリプトマガジンでは、小型コンピュータボード「Raspberry Pi」(ラズパイ)向けのセンサー搭載拡張ボード「ラズパイセンサーボード」を制作しました。第8回では、前回I/Oエキスパンダ「MCP23017」で増やしたGPIO 端子に7セグメントLEDを接続して制御します。

シェルスクリプトマガジン Vol.61は以下のリンク先でご購入できます。

図5 7セグメントLEDに数字を出力するクラスライブラリ(ssegled.py)

from mcpgpio import MCPGPIO

class LED7SEG():
  __leds = [  [0, 1, 2, 8, 9, 10],    # 0
              [2, 8],                 # 1
              [9, 8, 11, 0, 1],       # 2
              [9, 8, 11, 2, 1],       # 3
              [10,11, 2, 8],          # 4
              [9, 10, 11, 2, 1],      # 5
              [9, 10, 11, 2, 1, 0],   # 6
              [9, 8, 2],              # 7
              [0, 1, 2, 8, 9, 10, 11],# 8
              [1, 2, 8, 9, 10, 11]    # 9
          ]

  def __init__(self):
    self.gpio = MCPGPIO()
    self.gpio.setup(0, MCPGPIO.OUTPUT)
    self.gpio.setup(1, MCPGPIO.OUTPUT)
    self.gpio.setup(2, MCPGPIO.OUTPUT)
    self.gpio.setup(3, MCPGPIO.OUTPUT)
    self.gpio.setup(8, MCPGPIO.OUTPUT)
    self.gpio.setup(9, MCPGPIO.OUTPUT)
    self.gpio.setup(10, MCPGPIO.OUTPUT)
    self.gpio.setup(11, MCPGPIO.OUTPUT)

  def off(self):
    for l in self.__leds[8]:
      self.gpio.output(l, MCPGPIO.LOW)
  
  def print(self, n):
    if (n < 0) or (n > 9):
      return
      
    self.off()
    for l in self.__leds[n]:
      self.gpio.output(l, MCPGPIO.HIGH)

図6 テストスクリプト(count.py)

from ssegled import LED7SEG
import time

led = LED7SEG()

for i in range(10):
  led.print(i)
  time.sleep(1)

第8回 自動でアーカイビングする

投稿日:2019.07.16 | カテゴリー: 記事

 大容量のハードディスクやSSD(Solid State Drive)が安価に購入できても、パソコンやコンピュータ内のファイル整理は重要です。しかし、いざファイルを整理しようとして削除してもよいかどうか迷うものも多数あるでしょう。とりあえずは、まとめて圧縮することでアーカイブ(書庫)化して取っておくのがよいでしょう。

 そこで今回は、ファイルを自動でまとめて圧縮するアーカイビングのシェルスクリプトを作成します(図1)。

図1 ファイルをフォルダごとアーカイビング

第7回 DMを自動送信する(STARTTLS編)

投稿日:2019.07.2 | カテゴリー: 記事

 第6回では、「ダイレクトメール」(DM)を送信するときにメールサーバーへ接続するプロトコルとして「SMTPS」(SMTP over SSL/TLS)を用いました。もう一つ、安全にメールサーバーにメッセージを送信する著名なプロトコルとして「SMTP STARTTLS」があります。これは、完全に通信が暗号化されたSMTPSと違い、最初は平文で途中から暗号化するという通信方法が可能です。ちなみに、SMTPSでは「465」番のTCPポートを、SMTP STARTTLSでは「587」番のTCPポートを使用します。

 SMTP STARTTLSの場合、SMTPSとはアクセス方法が異なるので、シェルスクリプトの書き方が違います。そこで今回は、SMTP STARTTLSを対応したメールサーバーからDMを自動送信するシェルスクリプトを作成します(図1)。

図1 SMTP STARTTLS対応のメールサーバーからDM送信

  • shell-mag ブログの 2019年7月 のアーカイブを表示しています。

  • -->