こんにちは、 id:rightgo09 です。
比較演算子の左右とコメントの罠に嵌ったのでエントリの一つとして書いてみます。
続きを読むiOSエンジニアのしだです。最近はiOSを書いていませんが、iOSエンジニアと言い続けたいと思います。
TensorFlow Developer Summit 2018 にアナウンスされていた Swift for TensorFlow が先日公開されたのでさわってみます。
こんにちは。エンジニアの田渕です。 GWで最大9連休!なんて声も聞こえますが、人が少ない時ほど働きたい天邪鬼です。 (いや、単純に電車が空いてるからって話ですが。。。)
ユニファのエンジニアブログも、気づけば開始から一年以上が経過しています。 今回は、保育業界に携わるエンジニアとしての、この一年ほどの自分の活動を振り返ってみました。
ちょうど一年ほど前のことです。 この「ユニファ開発者ブログ」にて、こんな記事を書かせて頂きました。
この4月から実際に適用が開始されている新「保育所保育指針」に関する内容でした。 読んで頂くと分かる通り、当時出たばかりの新「保育所保育指針」の原文を読んだり、解説している講座に足を運んだり、関連したブログやWeb上の記事を読んだりと、この頃から色々なことを始めました。 自社サービスを持つ会社のエンジニアである以上、ある程度は「顧客」=「保育士や保護者の方々」に対しての理解は必要です。 しかし、これまで黙々と開発していたWebエンジニアがある日突然、保育関連の省庁の文章などを読み出したのですから、前職からの知り合いなどからは「何やってんの?」と盛大に突っ込まれました。
突然そんなことを始めたのには、当然理由がありました。 当時、エンジニアとして様々な機能の要件を定義したり、開発したりしていましたが、自分の中のそれまでの「普通」が通用しないという現実をひしひしと感じていたのです。
「これ、こうしたら喜んでくれるんじゃない?」と思って作ったものが喜んでもらえない。
「こうしたら使いやすくなる。」と思って作ったものが、「使いづらい」と言われる。
明らかに、保育士の方々の感覚に合わせられていませんでした。 新しいプロダクトや機能を開発する際にはヒアリング、テストも行いますので、感覚として分からなくてもそこで調整することで良いものは作れます。 ですが、持って行ったものが全然求められていない/的外れである場合、そこから良いものにするまでには、長い時間と手間がかかってしまいます。 完璧ではなくても、出来るだけ始めから及第点に近いものを持っていけるようになりたい。
「肌感覚として保育士の方々に喜んでもらえるものが分からない」というのは、当時の私には致命的に思えました。
コーディングなどにも当てはまることですが、何かを修正する際、あまりに変更量が多い場合は作り直した方が早かったりもします。 「自分の感覚を保育士の皆さんの感覚に近づける」という行為は、私にとってはまさにそれでした。 ちょっと微修正で辿り着ける気は全くしなかったので、これは一度自分の中の「普通」を壊してやり直そう、と思ったのです。
そんなわけで、黙々と保育所保育指針を読み、関連書籍を漁り、社外の勉強会に参加し、保育士の方々の「普通」を知るため、活動を始めました。
始めから長期戦になるだろうなぁと思っていたので、地道に、コツコツと……。。。
昨今、関連省庁からICTに関する補助金が出ている流れなどもあり、雑誌や勉強会などでも度々保育関連のシステムに関する話題が出ます。 勉強会では特に自分の身分を明かしている訳ではないので、一般的なシステムというものに対する保育士のみなさんの印象や感想をそのまま耳にすることもあります。
「うちの園では最近、なんかシステム導入したんだけど、使い慣れないから時間がかかって子供を見る時間が減った。」
「上の人たちが入れろって言うから入れたけど、全然良さが分からない。」
「これも世の流れなのかしらね。」
正直に言って、ネガティブな言葉の方が圧倒的に多いです。耳が痛い……と思うこともしばしば。 保育システム導入に関しての特集記事などを見ても、現場に近ければ近いものほど「これはもう、避けられない流れなのだから、頑張って受け入れよう!」という、どちらかといえば「諦めて受け入れよう」という論調が多い。 確かに、現時点で様々な技術を含むシステムが保育業界に対して出来ている貢献は、他業種と比べると圧倒的に少ないのです。
ですが、こうして地道な活動を始めて一年、始めた頃に書いた前述の記事を読むと、その頃に比べたら色々なことが感覚として分かるようになったなぁと感じています。 システムに対する印象がネガティブなものになりがちな理由も、わかってきました。
最近思うのは、恐らくはシステムを作っているエンジニアが見ている夢が、正しく保育士の皆さんに伝わってないんだな、ということ。 ともすれば、相反し、敵対するものとさえ考えられています。 たぶん、保育関連システムの開発に携わっているエンジニアの中で「保育士さんの仕事を邪魔しよう」なんて考えて開発している人は一人もいないのに、そんな風になってしまう現実は悲しくもありますが、システムを作っている我々の不理解や、力量不足、説明不足な面もあると思っています。
システム化の先で我々エンジニアが夢見ているもの=「本来の目的や将来像」がひとめで伝わり、受け入れてもらえるプロダクトが作れればいいんですが、……なかなか簡単にはいかないものです。
今後も、現場の保育士の方々の声に真摯に耳を傾けながら、少しでもお役に立てるものを作っていけたらと考えています。
それでは。
皆様こんにちは。ユニファの赤沼です。ユニファでは様々な職種で採用を続けていまして、今年に入ってさらにメンバーが増えています。今のオフィスには昨年11月に移転したばかりですが、早くもスペースが埋まって来ました。人数が増えて困ることの一つがトイレ事情ですね。特に今のオフィスでは男性トイレの個室が一つしかなく、行ってみたけど使用中で戻ってくるのを繰り返す、ということも発生するようになって来ました。そこで多少でも状況を改善できないかと、手元にあった機材でトイレセンサーを作ってみました。
サービスの全体像としては、個室の使用状況をセンサーで検知し、ステータスが変わった時に Slack に通知するというシンプルなものです。今のオフィスのトイレはドアの開閉は使用状況に連動しない(空室でもドアが閉めておける)のでどうやって使用状況を判定しようか迷ったのですが、電源を取れる場所の都合などもあり、ひとまず距離センサーで近くに何かを検知した場合は人が入って来たということで使用中のステータスとするようにしました。ステータスは空室(Vacant)か使用中(Occupied)のいずれかです。ネットワークへの接続はオフィスの AP が利用できる範囲だったので、 Wi-Fi で接続しています。
まずは回路図です。下記の図では作図素材の都合上 Raspberry Pi 3 を使っていますが、実際は Raspberry Pi Zero W を使用しています。
距離センサーとしては今回は超音波センサーを使用しています。
また、見た目で稼働状況がわかるように LED を配置しています。それぞれ下記のようにGPIOピンを接続します。
【距離センサー】
【LED】
それでは上記回路をしようするためのコーディングです。今回は Python で実装してみました。まずは距離センサーを扱うクラスを下記のように実装しました。コンストラクタでは使用するピンやロガーの設定をしています。超音波センサーの仕組みとしては、TRIGピンから出したパルスが対象物に反射してECHOピンで受け取れるまでの時間から距離を計算します。詳細については先ほどの製品ページのサンプルなどもご覧いただくとしてここでは割愛しますが、かかった時間を計算する calc_duration() メソッドと、それをベースに距離を計算する distance() メソッドを用意しています。
#!/usr/bin/env python import RPi.GPIO as GPIO import time from logging import getLogger, FileHandler, Formatter, DEBUG from timeout_decorator import timeout, TimeoutError class DistanceSensor: TRIG_PIN = 17 ECHO_PIN = 27 DEBUG_LOG_FILE = 'logs/distance_sensor_debug.log' TIMEOUT_SEC = 3 def __init__(self): GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(self.TRIG_PIN, GPIO.OUT) GPIO.setup(self.ECHO_PIN, GPIO.IN) self.debug_logger = getLogger(__name__ + '_Debug') debug_file_handler = FileHandler(self.DEBUG_LOG_FILE) formatter = Formatter('%(asctime)s:%(levelname)s:%(message)s') debug_file_handler.setFormatter(formatter) self.debug_logger.addHandler(debug_file_handler) self.debug_logger.setLevel(DEBUG) @timeout(TIMEOUT_SEC) def calc_duration(self): GPIO.output(self.TRIG_PIN, GPIO.LOW) time.sleep(0.3) GPIO.output(self.TRIG_PIN, GPIO.HIGH) time.sleep(0.00001) GPIO.output(self.TRIG_PIN, GPIO.LOW) pulse_off = None while GPIO.input(self.ECHO_PIN) == 0: pulse_off = time.time() if pulse_off is None: self.debug_logger.debug('pulse_off is None.') return None pulse_on = None while GPIO.input(self.ECHO_PIN) == 1: pulse_on = time.time() if pulse_on is None: self.debug_logger.debug('pulse_on is None.') return None duration = pulse_on - pulse_off return duration def distance(self): try: duration = self.calc_duration() if duration is None: return None distance = duration * 17000 return distance except TimeoutError: self.debug_logger.debug('TimeoutError while calcurating duration.') return None
また、 slack への通知処理も下記のようにクラスを分けて実装しています。APIのコールには Requests モジュールを使用しています。
#!/usr/bin/env python import json import requests class SlackNotifier: def __init__(self, webhook_url): self.webhook_url = webhook_url def notify(self, fallback, text, username, icon_emoji, channel, color): headers = { 'Content-Type': 'application/json' } payload = { 'username': username, 'channel': channel, 'icon_emoji': icon_emoji, 'attachments': [ { 'fallback': fallback, 'text': text, 'color': color } ] } response = requests.post(self.webhook_url, json = payload, headers = headers) return { 'status_code': response.status_code, 'text': response.text }
そしてこれらのクラスを使用して処理を行うメインのクラスを下記のように実装しています。1秒おきに距離を取得していますが、距離センサーの測定値にはばらつきがあり、温度や対象物の素材によっても音波を反射しやすいかどうかが変わってくるので、単発の測定値だけで判定するのではなく、直近10回分の測定結果から判定するようにしています。10回のうち8回以上閾値より近くに何かがあるという結果になったら使用中のステータスに変更し、2回以下になったら空室ステータスに変更します。3回〜7回の場合はステータスを変更しません。入室時や退室時は閾値を越えたり越えなかったりという感じになるので、その揺らぎを吸収するという意図もあります。閾値より近くに物を検知した場合は LED を点灯させます。ステータスが切り替わった場合には slack に通知します。
#!/usr/bin/env python import distance_sensor import slack_notifier import time import RPi.GPIO as GPIO from logging import getLogger, FileHandler, Formatter, DEBUG class RestroomMonitor: SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX' SLACK_USERNAME = 'RESTROOM_TOKYO_MENS' SLACK_EMOJI_ICON = ':restroom:' SLACK_CHANNEL = '#tokyo_restroom_mens' LOG_FILE = 'logs/restroom_monitor.log' THRESHOLD_CM = 108 OCCUPIED_THRESHOLD = 0.8 VACANT_THRESHOLD = 0.2 LED_PIN = 18 def __init__(self): self.logger = getLogger(__name__) file_handler = FileHandler(self.LOG_FILE) formatter = Formatter('%(asctime)s:%(levelname)s:%(message)s') file_handler.setFormatter(formatter) self.logger.addHandler(file_handler) self.logger.setLevel(DEBUG) self.distance_sensor = distance_sensor.DistanceSensor() self.slack_notifier = slack_notifier.SlackNotifier(self.SLACK_WEBHOOK_URL) GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) GPIO.setup(self.LED_PIN, GPIO.OUT) self.threshold_queue = [0] * 10 self.occupied = False def notify_to_slack(self, occupied): fallback = 'Occupied' if occupied else 'Vacant' text = 'Occupied' if occupied else 'Vacant' color = 'warning' if occupied else 'good' res = self.slack_notifier.notify( fallback, text, self.SLACK_USERNAME, self.SLACK_EMOJI_ICON, self.SLACK_CHANNEL, color ) self.logger.debug( 'Notified to Slack. STATUS_CODE - {status_code}, MSG - {msg}'.format( status_code = res['status_code'], msg = res['text'] ) ) def judge_occupation(self, distance): threshold_queue_value = 1 if distance <= self.THRESHOLD_CM else 0 GPIO.output(self.LED_PIN, GPIO.HIGH if threshold_queue_value == 1 else GPIO.LOW) self.threshold_queue.append(threshold_queue_value) del self.threshold_queue[0] average = sum(self.threshold_queue) / len(self.threshold_queue) if average >= self.OCCUPIED_THRESHOLD: occupied = True elif average <= self.VACANT_THRESHOLD: occupied = False else: occupied = self.occupied self.logger.debug( 'QUEUE - {queue} {distance} {average} {occupied}'.format( queue = self.threshold_queue, distance = distance, average = average, occupied = occupied ) ) return occupied def execute(self): while True: distance = self.distance_sensor.distance() if distance is None: self.logger.debug('Distance is None.') time.sleep(1) continue occupied = self.judge_occupation(distance) if self.occupied != occupied: self.occupied = occupied self.notify_to_slack(self.occupied) time.sleep(1)
実装が終わったら実際にトイレに設置します。個室内にAC電源があるのでそこから micro USB で給電し、トイレットペーパーホルダー上に配置します。ブレッドボードとラズパイの裏には養生テープを丸めて簡単に固定しています。
設置したら ssh でログインしてスクリプトを実行します。
$ ./restroom_monitor.py &
ログには下記のように出力されていきます。
2018-04-25 09:53:35,610:DEBUG:QUEUE - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 109.69352722167969 0.0 False 2018-04-25 09:53:36,924:DEBUG:QUEUE - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] 106.19568824768066 0.1 False 2018-04-25 09:53:38,236:DEBUG:QUEUE - [0, 0, 0, 0, 0, 0, 0, 0, 1, 1] 81.27307891845703 0.2 False 2018-04-25 09:53:39,551:DEBUG:QUEUE - [0, 0, 0, 0, 0, 0, 0, 1, 1, 1] 80.18684387207031 0.3 False 2018-04-25 09:53:40,862:DEBUG:QUEUE - [0, 0, 0, 0, 0, 0, 1, 1, 1, 1] 75.01912117004395 0.4 False 2018-04-25 09:53:42,174:DEBUG:QUEUE - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] 80.32464981079102 0.5 False 2018-04-25 09:53:43,486:DEBUG:QUEUE - [0, 0, 0, 0, 1, 1, 1, 1, 1, 1] 77.09026336669922 0.6 False 2018-04-25 09:53:44,798:DEBUG:QUEUE - [0, 0, 0, 1, 1, 1, 1, 1, 1, 1] 73.83155822753906 0.7 False 2018-04-25 09:53:46,110:DEBUG:QUEUE - [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] 76.49850845336914 0.8 True 2018-04-25 09:53:51,961:DEBUG:Notified to Slack. STATUS_CODE - 200, MSG - ok
ステータスが切り替わると下記のように slack に通知されます。
現状それなりに稼働させていますが、下記のような課題があります。
【精度】
空室状態の時に誤検知して使用中と判定することはほぼないのですが、使用中なのに空室と判定したり、空室と使用中の切り替えを繰り返してしまったりということが多く、もっと確実にステータスを判定できるセンシングに変更したいところです。
【ネットワーク】
オフィスの AP に届いてはいるものの、やはりそんなに電波は強くないので、状況によってはネットワークに接続できずにエラーになってしまうので、ネットワーク接続の安定性は改善したいと思っています。
【死活監視】
いつの間にかプロセスが落ちて通知が止まっていることがあるので、プロセス監視を入れて落ちたら自動的に起動するなどの対応が必要です。
まだまだ課題はあるものの、センサー設置前と比べると便利になっているということで、メンバーにはそれなりに好評のようです。今まで色々センサーを試してみたりはしていましたが、やっぱりちゃんと役に立つものを作れると面白いですね。課題点については暇を見て改善していこうと思います。
ハイドーモ!QAの山岸です。
最近腰を痛めてしまい、通院しています! なかなか治りが悪く歳を感じています。
今回のブログは技術ブログと言いつつも普段からオフィスで仕事をしている人が誰しもが悩む肩こり腰痛をテーマに書こうと思います!!
そもそも肩こり、腰痛の原因は??
オフィスで働く人が接骨院や整体に行くと怒られた!みたいな話を聞きます。
などなどwebで紹介されている記事から見ても座席に座って仕事をしている人はほとんどの項目が該当するとおもいます。
肩こり、腰痛への対策と対応
肩こり
肩こりの原因は運動不足による血行不良、眼精疲労や冷え性、普段の姿勢が主となっています。
ならないための対策となってしまったときの対処法
腰痛
こちらも肩こりと同様の対策になります。
こんにちは、Webエンジニアのちょうです。
最近A4サイズでも印刷できるように画面スタイルの調整をしてます。ところが、Chromeだけおかしいな挙動がありました。
Chrome
Firefox
9px, 8px, 7pxのところに注目してください。なぜかChromeだと10pxとほぼフォントサイズが一緒になってるように見えないでしょうか。実際はどうなんでしょう。開発者ツールで見ると
あれ、本当に10pxになっています。ちなみに、このような現象はChromeだけあります。Firefox以外に、SafariやEdge、IEまで普通に指定通りのフォントサイズになってます。もしかして、Chromeのバグ?
ありえないと思いながら、Google先生にきいてみました。実はChromeにこういう設定があります。
続きを読む