高専祭で作ったもの(とん平焼き班)
https://scrapbox.io/files/674c23c2ed1ef511fdeabf2a.png
▲柔道部とん平焼き
高専祭にて、セルフレジを作成した。
ファイルを以下の 図1 ~ 図4 に示す。
コメント・ご意見などあれば👇へ
このたび、専門家の方より、頑張ったで賞を授与いたしました。ありがとうございます。
-------------------------------------------------------------------
実はほぼコレと同じ仕組み
カードがバーコードになった感じ
https://scrapbox.io/files/67a9b977d88149a064b05ccc.png
code:barcode_server.py
import csv
import cv2
from pyzbar.pyzbar import decode
import winsound
import webbrowser
import threading
import time
import signal
import sys
from datetime import datetime
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from websocket_server import WebsocketServer
# CSVファイルを読み書きするところ
def read_product_data(barcode):
with open('product_data.csv', 'r', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
return None, None
def read_daily_total():
with open('daily_total.csv', 'r', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
def write_daily_total(daily_total):
with open('daily_total.csv', 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerow({'DAILYTOTAL': daily_total})
# バーコードをスキャンするところ
def scan_barcode(server):
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
request_type = 'RESET'
barcode_string = ''
product_name = ''
product_price = 0
total_price = 0
daily_total = read_daily_total()
# responseの初期化
response = f"{request_type}, {barcode_string}, {product_name}, {product_price}, {total_price}, {daily_total}"
server.send_message_to_all(response)
current_time = 0
last_scan_time = 0
while True:
request_type = 'SCAN'
ret, frame = cap.read()
if not ret:
continue
for barcode in decode(frame):
barcode_string = barcode.data.decode('utf-8')
current_time = time.time()
if current_time - last_scan_time < 1.0: # スキャンを1.0sec休憩させる
continue
product_name, product_price = read_product_data(barcode_string)
if product_name != None and product_price != None:
winsound.Beep(700, 200)
total_price += product_price
daily_total += product_price
write_daily_total(daily_total)
last_scan_time = time.time()
else:
winsound.Beep(500, 400) # バーコード未登録
product_name = None
product_price = 0
response = f"{request_type}, {barcode_string}, {product_name}, {product_price}, {total_price}, {daily_total}"
server.send_message_to_all(response)
cv2.imshow("Barcode Scanner", frame) # カメラ画面の更新
if cv2.waitKey(1) & 0xFF == ord(' '): # スペースキーで金額精算
total_price = 0
request_type = 'RESET'
response = f"{request_type}, {barcode_string}, {product_name}, {product_price}, {total_price}, {daily_total}"
server.send_message_to_all(response)
if cv2.waitKey(1) & 0xFF == ord('q'): # Qキーで強制終了
break
cap.release()
cv2.destroyAllWindows()
# WebSocketとかをいろいろするところ(ほぼコピペ)
def new_client(client, server):
print("Client has connected.")
def start_websocket_server():
websocket_server = WebsocketServer(port=8765, host='localhost')
websocket_server.set_fn_new_client(new_client)
print("The Websocket-Server has been activated.")
# バーコードスキャンスタート
scan_thread = threading.Thread(target=scan_barcode, args=(websocket_server,))
scan_thread.start()
websocket_server.run_forever()
class HTTPRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/':
self.path = '/index.html'
try:
with open(self.path1:, 'rb') as file: self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
self.wfile.write(file.read())
except FileNotFoundError:
self.send_response(404)
self.end_headers()
def start_http_server():
httpd = ThreadingHTTPServer(('localhost', 8000), HTTPRequestHandler)
print("The Http-Server has been activated.")
httpd.serve_forever()
def signal_handler(sig, frame):
print("Shutdown")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
# 最初に実行されるところ
if __name__ == "__main__":
http_thread = threading.Thread(target=start_http_server)
websocket_thread = threading.Thread(target=start_websocket_server)
http_thread.start()
websocket_thread.start()
time.sleep(1)
code:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>バーコードスキャナ</title>
<style>
body {
font-family: 'M PLUS 1p', Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.container {
text-align: left;
}
.font_space {
letter-spacing: 0.1em
}
h1 {
position: relative;
padding: 0.6em;
}
h1:after {
position: absolute;
content: '';
top: 100%;
left: 30px;
border: 15px solid transparent;
width: 0;
height: 0;
}
margin-top: 20px;
padding: 15px;
border-radius: 10px;
}
</style>
</head>
<body>
<div class="container">
<h1 class ="font_space">「セルフレジ」バーコードスキャン</h1>
<h2 class ="font_space">⇦⇦バーコードをスキャンしてください</h2>
<p>スペースキーを押すと、高専祭での売上が更新されます。<br>めざせ累計100万円!</p>
<div id="product-info">
<p class ="font_space">バーコード: <span id="barcode">................</span></p>
<p class ="font_space">商品名: <span id="product-name">................</span></p>
<p class ="font_space">価格: <span id="product-price">0</span> 円</p>
<p class ="font_space">合計価格: <span id="total-price">0</span> 円</p>
<p class ="font_space">高専祭での売上: <span id="daily-total">0</span> 円</p>
</div>
</div>
<script>
const websocket = new WebSocket("ws://localhost:8765");
websocket.onmessage = function(event) {
if (requestType === "RESET") {
document.getElementById("total-price").textContent = totalPrice;
document.getElementById("daily-total").textContent = dailyTotal;
} else {
document.getElementById("barcode").textContent = barcode;
document.getElementById("product-name").textContent = productName;
document.getElementById("product-price").textContent = productPrice;
document.getElementById("total-price").textContent = totalPrice;
}
};
function requestBarcode() {
websocket.send("scan");
}
websocket.onerror = function() {
alert("A Websocket error has occurred. The server may not be running.");
};
websocket.onclose = function() {
alert("WebSocket Closed.");
};
</script>
</body>
</html>
code:product_data.csv
BARCODE,NAME,PRICE
4902555117492,不二家 ミルキー,60
4902777200514,明治 ピスタチオチョコレート,238
4901330647384,Jagabee うすしお味,258
4902888255335,森永 キャラメル,150
4902777049304,明治 きのこの山いちご,148
4902777040905,明治 たけのこの里いちご,148
4901005512672,グリコ ポッキーチョコレート,138
code:daily_total.csv
DAILYTOTAL
692692