基本情報技術者試験に合格しました
本日基本情報を受けてきました. CBTに移行してからはスコアレポートが受験後すぐに見れるようになっているのですが,午前・午後どちらも6割を超えており合格していると思います.
(2022/07/18追記) 合格証書がお家に送付されて来ました。合格のようです。
仕事が思ったより忙しくあまり勉強の時間が取れませんでしたが,受かってよかったです.
次は応用情報取得を目指して勉強していこうと思います.
本日基本情報を受けてきました. CBTに移行してからはスコアレポートが受験後すぐに見れるようになっているのですが,午前・午後どちらも6割を超えており合格していると思います.
(2022/07/18追記) 合格証書がお家に送付されて来ました。合格のようです。
仕事が思ったより忙しくあまり勉強の時間が取れませんでしたが,受かってよかったです.
次は応用情報取得を目指して勉強していこうと思います.
Djangoでマークダウン形式のエディタを爆速で実装します.参考にしたサイトは以下です.
Markdown形式のエディタをDjangoに爆速で実装できるライブラリです.ここ 実際に動作しているところはリンク先をご覧ください.結構有能なのでびっくりします.
まず,pipで必要なライブラリを入れましょう
pip install django-mdeditor
pip install Markdown
mdeditor自体の設定をしていきます.settings.pyに以下を追記します.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'mdeditor' #これを追記
]
X_FRAME_OPTIONS = 'SAMEORIGIN'
MDEDITOR_CONFIGS = {
'default': {
'language': 'en',
}
}
次に,models.pyを設定していきましょう.app内で定義するModelを以下のように定義します. 普段使っているmodels.TextFieldが Markdown形式のオブジェクトになります
#models.py
from django.db import models
from mdeditor.fields import MDTextField
# Create your models here.
class Article(models.Model):
title = models.TextField(max_length=200)
content = MDTextField() #ここがMarkdown形式のTextFieldになる
def __str__(self):
return self.title
また,{project_name}/urls.pyに以下を追記します.
urlpatterns = [
path('admin/', admin.site.urls),
path('mdeditor/', include('mdeditor.urls')) #これを追記
]
admin.pyへの追記を忘れずに!
# admin.py
from django.contrib import admin
from .models import Article
# Register your models here.
admin.site.register(Article)
現時点でDjango Adminの管理画面からMarkdown形式で編集できるようになっているはずです.
次に,MarkdownをHTMLに変換していい感じに表示されるようにしていきましょう. 先ほど定義したArticleモデルに次を追記していきます.
#models.py
from django.db import models
from mdeditor.fields import MDTextField
import markdown #ここを追記
# Create your models here.
class Article(models.Model):
title = models.TextField(max_length=200)
content = MDTextField() #ここがMarkdown形式のTextFieldになる
def __str__(self):
return self.title
# 以下を追記
def markdown_to_html(self):
md = markdown.Markdown(
extensions=['extra', 'admonition', 'sane_lists', 'toc']
)
html = md.convert(self.content)
return html
このメソッドをHTML内のDjangoテンプレートから呼び出していきます. 以下は関連する部分のHTMLコードです.
<!doctype html>
{% extends 'base.html' %}
{% load static %}
{% block content %}
{% for item in object_list %}
<article class="blog-post">
<h4 class="blog-post-title">{{ item.title }}</h4>
{{ item.markdown_to_html|safe }}
</article>
{% endfor %}
{% endblock %}
これだけだとコード整形のみが為されてお洒落なシンタックスハイライトがつきません. いい感じの見栄えにするために以下をHTML内に追記します.
<head>
...
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/styles/a11y-dark.min.css">
...
</head>
<body>
...
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.6/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
これで完了です.試しにサーバーを立ち上げて確かめてみてください.
python3 manage.py runserver
お久しぶりです。IMAXおじさんです。 今日は今までのWEB系の話とは少し変わって、PyModbusでModbus/TCPのサーバーを構築する話をします。
HMSのサイト M-system技研のModbus/TCP通信仕様書
Modbus/TCPとは、Modicon社(現Schneider Electric社)が1979年に策定した産業用Ethernetプロトコルです。 主にPLCやリモートIOとの通信に使用される産業用Ethernetプロトコルであり、世界的なシェアとしては5%程度を占めています(2020年現在)。
サーバー(フィールド側, PLCやリモートIO)
xxx.yyy.zzz.100
|
|
クライアント(PC)
xxx.yyy.zzz.101
Modbus/TCPはサーバー/クライアント方式の通信プロトコルです。 PLCやリモートIOなどがスレーブ(==サーバー)、データ収集を行うPCなどがマスター(==クライアント)としてデータのやり取りを行います。 名前の通り、各サーバーとクライアントがTCPでペイロードをやり取りします。 物理レイヤーの仕様(端子形状など)は指定されておらず、あくまで通信のレイヤーのみの仕様となります。
ペイロードの仕様に関しては、下記のM-system技研発行の通信仕様書がよくまとまっており参考になります。
PyModbusとは上記のModbusサーバー・クライアントを擬似的にPythonで構築できるライブラリです。 対向機をわざわざ購入するとなると、数万円かかってしまいますが、PyModbusを使えば無料で手軽に対向機を用意できます。
さて、Modbus/TCPの説明が終わったところで早速デモの構築に移りましょう。 PC上にPyModbusで構築したサーバーまでデータを取りに行くデモを構築します。
PyModbusのサーバーを早速構築します。ローカルホストにサーバーを立てることにするため、特にIPアドレスは気にしなくてもよいです。 実際にデバイス間通信を試したい人は、Ethernetポートを備えたデバイスを2個用意して固定IPを振ってあげるとよいかと思います。
今回はinput_register
に値を入れることにします。
サーバーがレジスタ(input_register
)に保持する値は、以下の通りとしましょう。
----------------------------
register1 | int16
----------------------------
register2 | float
register3 | (32bit)
----------------------------
Modbusでは、1レジスタ=2byte(16bit)で扱われることに注意してください。なので、32bitのfloatなど16bitを超えるようなデータはレジスタをまたがって保持されることになります。
さて、このような値を返してくれるサーバーをPyModbusで構築していきます。まずは必要ライブラリのimportを。
# sample_server.py
from pymodbus.constants import Endian
from pymodbus.version import version
from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.payload import BinaryPayloadBuilder
サーバー側でアクセス・送受信しているパケットの内容を確認するため、ログの設定をします。
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
次に、レジスタに入れる値を作っていきます。
PyModbusではpymodbus.BinaryPayloadBuilder
を使ってペイロードのオブジェクトを作ることになります。
builder = BinaryPayloadBuilder(byteorder=Endian.Big) #ペイロードビルダーのインスタンスを作成。バイトオーダーは再現したいリモートIOの仕様に準ずること。
# 最初のint_16のペイロードを作成
builder.add_16bit_int(100)
# 2つ目のfloatのペイロードを作成
builder.add_32bit_float(123.45)
# ペイロードをModbusSequentialDataBlockへ変換
block = ModbusSequentialDataBlock(1, builder.to_registers())
# レジスタへの登録を行う
store = ModbusSlaveContext(ir=block, zero_mode=False)
context = ModbusServerContext(slaves=store, single=True)
最後に、サーバーを起動しておしまいです。
StartTcpServer(context, address=("0.0.0.0, 502")) # ローカルホストで起動
サーバーの構築はできたので、サーバーにためている情報を取りに行くクライアントを作ります。
Modbusではクライアントからのみ通信を始めることができます。
クライアントから打てるリクエストにはいくつか種類(ファンクションコード)があります。
詳しくはModbus/TCPの通信仕様書を見ていただきたいのですが、今回はファンクションコード4(FC4)を使います。
FC4はread_input_registers
というリクエストになっており、その名の通りinput_registers
の値を読み取るものになります。
(余談ですが、Modbus/TCPではread_input_registers
で読まれるレジスタアドレスは300001
から始まると決まっているようです。FC4なのに開始アドレスが3xxxなのは気持ちが悪い...)
サーバーの設定では、レジスタ3つにまたがってデータが溜まっているのでread_input_registers
で3つ分レジスタを読んであげましょう。
from pymodbus.client.sync import ModbusTcpClient
from pymodbus.constants import Endian
from pymodbus.payload import BinaryPayloadDecoder
# Modbusクライアントのインスタンス。ローカルホストにサーバーが立っているので、アクセス先をlocalhostと指定。
client = ModbusTcpClient("localhost", port=502)
# FC4のread_input_registersでレジスタアドレス300001+0 ~ 300001+2を読み取る。
# 第一引数が開始アドレス(相対)、第二引数がレジスタ数
res = client.read_input_registers(0, 3, unit=1)
# 読み取ったレジスタ値をデコーダインスタンスへ渡す
# サーバー側がビッグエンディアン設定なので、こちらも合わせる。
decoder = BinaryPayloadDecoder.fromRegisters(res.registers, byteorder=Endian.Big)
# 最初のint_16bitをデコード
int16 = decoder.decode_16bit_int()
# 次のfloat_32bitをデコード
float32 = decoder.decode_32bit_float()
print(int16) # -> 100
print(float32) # -> 123.45...
これで、サーバーのinput_registers
に溜まったデータをModbus/TCPで読み取ることができました。
産業用ネットワーク難しい
nginx + uwsgi + django on dockerでサイトを構築し,localhost上で公開するまでの解説です.
djangoにはpython3 manage.py runserver
で起動できる便利な開発サーバー機能が備わっています.
しかし,本番環境ではこの機能は使わずに,apacheやnginxなどのwebサーバーを使用することが推奨されています.
今回は,nginxを使用してdjangoアプリケーションデプロイすることにします.
また,環境の移植性を考慮して,dockerを使用したデプロイを行います.
nginx + uwsgi + djangoの具体的な処理の流れはこちらで解説されていますので,ぜひ参照してください. 非常に分かりやすく書かれており,これを読むだけである程度の設定はできてしまいます.
Setting up Django and your web server with uWSGI and nginx
簡単に図解すると,下の図のようにリクエストが流れていくことになります.
webクライアント -> nginx(80,443ポート) -> uwsgi(8000ポート) -> djangoバックエンド
この構成を実現するため,以下ではnginx, uwsgi, djangoの各項目の設定事項を見ていきます.
docker-compose.yml
の中身まず,全体の見通しをよくするために,docker-compose.yml
の中身を紹介しておきます.
version: "3"
services:
nginx:
image: nginx:latest
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
- ./django-project/myapp/static:/static
- ./django-project/myapp/media:/media
ports:
- 8000:80
environment:
- LANG=C.UTF-8
- LANGUAGE=en_US
depends_on:
- django-project
- mysql
django-project:
build: ./django-project
restart: always
command: bash /init.sh
tty: true
volumes:
- ./django-project/app:/myapp
- ./django-project/init.sh:/init.sh
environment:
- DEBUG=False
- LOCALHOST=localhost
- DEPLOYHOST=#デプロイ先のドメイン名
depends_on:
- mysql
- redis-server
mysql:
# mysqlに関する記述
....
project/
|__django-project/
| |__myapp/
| | |__myapp/
| | |__static/
| | |__media/
| | |__manage.py
| |__Dockerfile
|__nginx
| |__conf/nginx.conf
| |__uwsgi_params
|__mysql
|__docker-compose.yml
まずnginxの設定を行います.
ホストOS側で./nginx/conf/nginx.conf
を作成し,に以下の項目を書き足していきます.
upstream django {
ip_hash;
server django-project:8000;
}
server {
listen 80;
charset utf-8;
client_max_body_size 75M;
location /static {
alias /static;
}
location /media {
alias /media;
}
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
server_tokens off;
上記のポイントは,以下です.
listen 80
で80番ポートをリッスンlocation /static, location /media
で静的ファイルの場所を指定.docker-compose.yml
内でマウントした/static, /media
内のファイルを探します.location /
でuwsgiをリクエストを流す先として指定します.uwsgi_params
はここからコピペで問題ありません.uwsgiはdjangoと同じdockerイメージの中に入れます../django-project/Dockerfile
内に以下の内容を書き加えます.
FROM python:3.8-slim-buster
# djangoなど必要なライブラリをインストール
COPY ./requirements.txt /requirements.txt
# RUN source /venv/bin/activate
RUN python3.8 -m pip install --upgrade pip
RUN python3.8 -m pip install -r requirements.txt
RUN python3.8 -m pip install uwsgi # uwsgiを入れること
これでdjangoをuwsgiで動かす準備はできました. 次にuwsgiでリクエストを待ち受けるために,コンテナ起動時に実行するシェルスクリプトを書いていきます.
#!/bin/bash
cd /myapp;
bash -c "python3 manage.py makemigrations";
bash -c "python3 manage.py migrate";
bash -c "python3 manage.py collectstatic --noinput"
bash -c "uwsgi --socket :8000 --module myapp.wsgi --py-autoreload 1s";
uwsgi(8000番ポート)で待ち受けることを最後の行で書き加えています.
最後に,djangoの設定を行なっていきます.
変更が必要なのは,/myapp/myapp/settings.py
のみです.
docker-compose.yml
のenvironment
でDEBUG=True/False
と指定することでデバッグモードが変えられるように,環境変数の読み込み部分を追記します.
また,djangoはDEBUG=False
の際には外部からの通信を受け付けないので,ALLOWED_HOSTS
にも環境変数の読み込みを追加します.
DEPLOYHOST
の部分にデプロイするサーバーのホスト名を書きます.# settings.py
# SECURITY WARNING: don't run with debug turned on in production!
is_debug_mode = os.environ.get("DEBUG")
DEBUG = (is_debug_mode == "True")
ALLOWED_HOSTS = [os.environ.get("LOCALHOST"), os.environ.get("DEPLOYHOST")]
STATIC_ROOT
とMEDIA_ROOT
を指定することで,DEBUG=False
の際に静的ファイルはnginxが直接サーブするようにできます.# settings.py
STATIC_URL = '/static/'
STATICFILE_DIRS = [
os.path.join(BASE_DIR, 'userctrl/static/'),
os.path.join(BASE_DIR, 'contest_manage/static/'),
os.path.join(BASE_DIR, 'ace-builds')
]
STATIC_ROOT = os.path.join(BASE_DIR, "static")
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/media/"
alpine linuxイメージをプロキシ環境下で使用する場合、様々な障壁に阻まれてコケることがあります。 以下では、その回避策を列挙していきます。(問題の切り分けができていないので一部おかしいことを言っているかもしれません)
Dockerfile内でプロキシを登録します。
ここでRUN export http_proxy={プロキシ:ポート}
とすると、なぜか環境変数に反映されておらずハマりました。
解決策としては
ENV http_proxy=http://{プロキシのIPアドレス}:{ポート}/
ENV https_proxy=http://{プロキシのIPアドレス}:{ポート}/
とすることで環境変数に反映することができました。
プロキシを経由する関係でapkレポジトリからライブラリを引っ張ってこれない場合があります。
これは、デフォルトでapkがインデックスを探しに行くレポジトリURLがhttps://~
となっている場合に起こります。
下記のissueにはdl-cdn.alpinelinux.org does not support TLS at all. (中略) APK repositories should be http onlyとあります。
そのため、/etc/apk/repositories
にプレーンなhttpのURLの記述とする必要があります。
すなわち、
RUN rm /etc/apk/repositories
RUN echo "http://cd-cdn.alpinelinux.org/alpine/v3.13/main" >> /etc/apk/repositories
RUN echo "http://cd-cdn.alpinelinux.org/alpine/v3.13/community" >> /etc/apk/repositories
とし、httpでapkのレポを探しに行きます。