New scanners added: Chrome + Smart Phone with JS library
This commit is contained in:
		
							
								
								
									
										226
									
								
								booker/templates/booker/chrome_scanner.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								booker/templates/booker/chrome_scanner.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,226 @@
 | 
				
			|||||||
 | 
					{% extends "base.html" %}
 | 
				
			||||||
 | 
					{% load static %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block additional_css %}
 | 
				
			||||||
 | 
					    <link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/normalize.css@8.0.0/normalize.css">
 | 
				
			||||||
 | 
					    <link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/milligram@1.3.0/dist/milligram.min.css">
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}Smartphone Scanner{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    <div class="container" >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="row-mb8" id="video-button-content">
 | 
				
			||||||
 | 
					            <a class="button" id="startButton">Start</a>
 | 
				
			||||||
 | 
					            <a class="button-outline" id="resetButton">Reset</a>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row" id="video-content">
 | 
				
			||||||
 | 
					            <div class="col-6">
 | 
				
			||||||
 | 
					                <video id="video" width="450" height="320" style="border: 1px solid gray"></video>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="col-2" id="sourceSelectPanel" style="display:none">
 | 
				
			||||||
 | 
					                <label for="sourceSelect">Change video source:</label>
 | 
				
			||||||
 | 
					                <select id="sourceSelect" style="max-width:400px">
 | 
				
			||||||
 | 
					                </select>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <div class="col-2">
 | 
				
			||||||
 | 
					                <label>Result:</label>
 | 
				
			||||||
 | 
					                <pre><code id="result"></code></pre>
 | 
				
			||||||
 | 
					                <ul id="barcode-list"></ul>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <script type="text/javascript">
 | 
				
			||||||
 | 
					    function beep() {
 | 
				
			||||||
 | 
					        var audio = new Audio("{% static 'sound/beep.mp3' %}");
 | 
				
			||||||
 | 
					        audio.volume = 0.1
 | 
				
			||||||
 | 
					        console.log("Audio play {% static 'sound/beep.mp3' %}")
 | 
				
			||||||
 | 
					        audio.play();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // check compatibility
 | 
				
			||||||
 | 
					    if (!('BarcodeDetector' in window)) {
 | 
				
			||||||
 | 
					      console.log('Barcode Detector is not supported by this browser.');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.log('Barcode Detector supported!');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // create new detector
 | 
				
			||||||
 | 
					      // var barcodeDetector = new BarcodeDetector({formats: ['code_128', 'code_39', 'code_93', 'codabar', 'data_matrix', 'ean_13', 'ean_8', 'qr_code', 'upc_a', 'upc_e']});
 | 
				
			||||||
 | 
					      var barcodeDetector = new BarcodeDetector({formats: ['code_128', 'ean_13']});
 | 
				
			||||||
 | 
					      console.log('Supported code formats:');
 | 
				
			||||||
 | 
					      BarcodeDetector.getSupportedFormats().then(supportedFormats => {
 | 
				
			||||||
 | 
					        supportedFormats.forEach(format => console.log(format));
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      console.log('end-of-format-list');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async function detect() {
 | 
				
			||||||
 | 
					        const mediaStream = await navigator.mediaDevices.getUserMedia({
 | 
				
			||||||
 | 
					            video: {facingMode: 'environment'}
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        const video = document.getElementById('video');
 | 
				
			||||||
 | 
					        // const video = document.createElement('video');
 | 
				
			||||||
 | 
					        let itemsFound = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        video.srcObject = mediaStream;
 | 
				
			||||||
 | 
					        video.autoplay = true;
 | 
				
			||||||
 | 
					        const list = document.getElementById('barcode-list');
 | 
				
			||||||
 | 
					        // list.before(video);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function render() {
 | 
				
			||||||
 | 
					            barcodeDetector
 | 
				
			||||||
 | 
					                .detect(video)
 | 
				
			||||||
 | 
					                .then(barcodes => {
 | 
				
			||||||
 | 
					                    barcodes.forEach(barcode => {
 | 
				
			||||||
 | 
					                        if (!itemsFound.includes(barcode.rawValue)) {
 | 
				
			||||||
 | 
					                            itemsFound.push(barcode.rawValue);
 | 
				
			||||||
 | 
					                            const li = document.createElement('li');
 | 
				
			||||||
 | 
					                            li.innerHTML = barcode.rawValue;
 | 
				
			||||||
 | 
					                            list.appendChild(li);
 | 
				
			||||||
 | 
					                            console.log('Got scan ' + barcode.rawValue);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    // beep();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .catch(console.error);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        (function renderLoop() {
 | 
				
			||||||
 | 
					            requestAnimationFrame(renderLoop);
 | 
				
			||||||
 | 
					            render();
 | 
				
			||||||
 | 
					        })();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.addEventListener('load', function () {
 | 
				
			||||||
 | 
					        detect();
 | 
				
			||||||
 | 
					        document.getElementById('resetButton').addEventListener('click', () => {
 | 
				
			||||||
 | 
					            document.getElementById('result').textContent = '';
 | 
				
			||||||
 | 
					            codeReader.reset();
 | 
				
			||||||
 | 
					            console.log('Reset.')
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    </script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<hr />
 | 
				
			||||||
 | 
					<div class="row mb-3">
 | 
				
			||||||
 | 
					    <div class="col-8">
 | 
				
			||||||
 | 
					        <div class="input-group">
 | 
				
			||||||
 | 
					            <input class="form-control" type="text" maxlength="40" id="container_id" placeholder="Container ID">
 | 
				
			||||||
 | 
					            <input class="form-control" type="text" maxlength="40" id="asset_id" placeholder="Asset ID">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="col-2">
 | 
				
			||||||
 | 
					        <div class="input-group">
 | 
				
			||||||
 | 
					            <input class="btn btn-primary" id="book-in-button" type="button" value="+Book">
 | 
				
			||||||
 | 
					            <input class="btn btn-primary" id="book-out-button" type="button" value="-Book">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="col-8">
 | 
				
			||||||
 | 
					        <div class="input-group">
 | 
				
			||||||
 | 
					            <textarea class="form-control" readonly="readonly" rows="4" id="container_details"></textarea>
 | 
				
			||||||
 | 
					            <textarea class="form-control" readonly="readonly" rows="4" id="asset_details"></textarea>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<div class="row">
 | 
				
			||||||
 | 
					    <div class="input-group">
 | 
				
			||||||
 | 
					        <textarea class="form-control mb-2" id="chat-log" cols="100" rows="10"></textarea>
 | 
				
			||||||
 | 
					        <input class="btn btn-secondary" id="clear-chat-area" type="button" value="Clear">
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					    var containerIsValid = false;
 | 
				
			||||||
 | 
					    var assetIsValid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async function getContainerInfo(containerId) {
 | 
				
			||||||
 | 
					        document.querySelector('#container_id').value = containerId;
 | 
				
			||||||
 | 
					        url = '/api/containers?named_id=' + containerId;
 | 
				
			||||||
 | 
					        document.querySelector('#container_details').value = 'Trying ' + url;
 | 
				
			||||||
 | 
					        const response = await fetch(url);
 | 
				
			||||||
 | 
					        const container = await response.json();
 | 
				
			||||||
 | 
					        if (container.count >= 1) {
 | 
				
			||||||
 | 
					            document.querySelector('#container_details').value = container.results[0].description;
 | 
				
			||||||
 | 
					            containerIsValid = true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            document.querySelector('#container_details').value = document.querySelector('#container_details').value + "\nERROR: No result\n";
 | 
				
			||||||
 | 
					            console.log(response);
 | 
				
			||||||
 | 
					            containerIsValid = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async function getAssetInfo(assetId) {
 | 
				
			||||||
 | 
					        document.querySelector('#asset_id').value = assetId;
 | 
				
			||||||
 | 
					        url = '/api/assets?named_id=' + assetId;
 | 
				
			||||||
 | 
					        document.querySelector('#asset_details').value = 'Trying ' + url;
 | 
				
			||||||
 | 
					        const response = await fetch(url);
 | 
				
			||||||
 | 
					        const asset = await response.json();
 | 
				
			||||||
 | 
					        if (asset.count >= 1) {
 | 
				
			||||||
 | 
					            document.querySelector('#asset_details').value = asset.results[0].description;
 | 
				
			||||||
 | 
					            assetIsValid = true;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            document.querySelector('#asset_details').value = document.querySelector('#asset_details').value + "\nERROR: No result\n";
 | 
				
			||||||
 | 
					            console.log(response);
 | 
				
			||||||
 | 
					            assetIsValid = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function useScannedText(msg) {
 | 
				
			||||||
 | 
					        // const data = JSON.parse(e.data);
 | 
				
			||||||
 | 
					        // msg = String(data.message);
 | 
				
			||||||
 | 
					        document.querySelector('#chat-log').value += (msg + '\n');
 | 
				
			||||||
 | 
					        if (msg.startsWith('C-')) {
 | 
				
			||||||
 | 
					            getContainerInfo(data.message);
 | 
				
			||||||
 | 
					            // document.querySelector('#container_id').value = msg;
 | 
				
			||||||
 | 
					        } else if (msg.startsWith('A-')) {
 | 
				
			||||||
 | 
					            getAssetInfo(data.message);
 | 
				
			||||||
 | 
					            // document.querySelector('#asset_id').value = msg;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            assetId = 'A-' + msg;
 | 
				
			||||||
 | 
					            document.querySelector('#asset_id').value = assetId;
 | 
				
			||||||
 | 
					            getAssetInfo(assetId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.querySelector('#clear-chat-area').onclick = function(e) {
 | 
				
			||||||
 | 
					        document.querySelector('#chat-log').value = "cleared\n";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.querySelector('#book-in-button').onclick = function(e) {
 | 
				
			||||||
 | 
					        if (!containerIsValid) {
 | 
				
			||||||
 | 
					            msg = 'Cannot book-in as container is invalid';
 | 
				
			||||||
 | 
					            console.log(msg);
 | 
				
			||||||
 | 
					            document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!assetIsValid) {
 | 
				
			||||||
 | 
					            msg = 'Cannot book-in as asset is invalid';
 | 
				
			||||||
 | 
					            console.log(msg);
 | 
				
			||||||
 | 
					            document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    document.querySelector('#book-out-button').onclick = function(e) {
 | 
				
			||||||
 | 
					        if (!containerIsValid) {
 | 
				
			||||||
 | 
					            msg = 'Cannot book-out as container is invalid';
 | 
				
			||||||
 | 
					            console.log(msg);
 | 
				
			||||||
 | 
					            document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!assetIsValid) {
 | 
				
			||||||
 | 
					            msg = 'Cannot book-out as asset is invalid';
 | 
				
			||||||
 | 
					            console.log(msg);
 | 
				
			||||||
 | 
					            document.querySelector('#chat-log').value += ('ERROR: ' + msg + '\n');
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -56,7 +56,8 @@
 | 
				
			|||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="row">
 | 
					    <div class="row">
 | 
				
			||||||
        <h1>Use your smartphone</h1>
 | 
					        <h1>Use your smartphone</h1>
 | 
				
			||||||
        <p>Use your <a class="navbar-brand" href="{% url 'booker:smartphone_scanner' %}">smartphone</a> to scan.</p>
 | 
					        <p>Use your <a href="{% url 'booker:smartphone_scanner' %}">smartphone</a> to scan.</p>
 | 
				
			||||||
 | 
					        <p>Use your <a href="{% url 'booker:chrome_scanner' %}">chrome Barcode-Scanner to scan.</p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,118 +2,223 @@
 | 
				
			|||||||
{% load static %}
 | 
					{% load static %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block additional_css %}
 | 
					{% block additional_css %}
 | 
				
			||||||
    <link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/normalize.css@8.0.0/normalize.css">
 | 
					 | 
				
			||||||
    <link rel="preload" as="style" onload="this.rel='stylesheet';this.onload=null" href="https://unpkg.com/milligram@1.3.0/dist/milligram.min.css">
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block title %}Smartphone Scanner{% endblock %}
 | 
					{% block title %}Smartphone Scanner{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
    <div class="container" >
 | 
					    <style>
 | 
				
			||||||
 | 
					        #interactive canvas { position: absolute; left: 0; }
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="row-mb8" id="video-button-content">
 | 
					    <div class="container" >
 | 
				
			||||||
            <a class="button" id="startButton">Start</a>
 | 
					        <div class="row" id="video-button-content">
 | 
				
			||||||
            <a class="button-outline" id="resetButton">Reset</a>
 | 
					            <div class="col-1">
 | 
				
			||||||
 | 
					                <a class="btn btn-primary" id="startButton">Start</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="col-1">
 | 
				
			||||||
 | 
					                <a class="btn btn-outline-primary" id="resetButton">Reset</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="col-1">
 | 
				
			||||||
 | 
					                <a class="btn btn-outline-primary" id="clearOverlayButton">Clear</a>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
        <div class="row" id="video-content">
 | 
					        <div class="row" id="video-content">
 | 
				
			||||||
            <div class="col-6">
 | 
					            <div id="interactive" class="viewport col-12" style="position: relative;">
 | 
				
			||||||
                <video id="video" width="450" height="320" style="border: 1px solid gray"></video>
 | 
					                <!-- QuaggaJS will populate this element -->
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					            <!--
 | 
				
			||||||
            <div class="col-2" id="sourceSelectPanel" style="display:none">
 | 
					            <div class="col-8">
 | 
				
			||||||
 | 
					                <video id="video" autoplay="true" width="640" height="480" style="border: 1px solid gray"></video>
 | 
				
			||||||
 | 
					            </div>-->
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="row" id="control-content">
 | 
				
			||||||
 | 
					            <div class="col-6" id="sourceSelectPanel" style="display:none">
 | 
				
			||||||
 | 
					                <div>
 | 
				
			||||||
                    <label for="sourceSelect">Change video source:</label>
 | 
					                    <label for="sourceSelect">Change video source:</label>
 | 
				
			||||||
                <select id="sourceSelect" style="max-width:400px">
 | 
					                    <select id="sourceSelect" style="max-width:400px"></select>
 | 
				
			||||||
                </select>
 | 
					 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <div class="col-2">
 | 
					            <div class="col-6" id="scan-result-panel" style="display:none">
 | 
				
			||||||
                <label>Result:</label>
 | 
					                <div>
 | 
				
			||||||
 | 
					                    <label>Scan:</label>
 | 
				
			||||||
                    <pre><code id="result"></code></pre>
 | 
					                    <pre><code id="result"></code></pre>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <script type="text/javascript" src="https://unpkg.com/@zxing/library@latest"></script>
 | 
					
 | 
				
			||||||
 | 
					<script type="text/javascript" src="{% static 'quagga/dist/quagga.js' %}"></script>
 | 
				
			||||||
<script type="text/javascript">
 | 
					<script type="text/javascript">
 | 
				
			||||||
    function beep() {
 | 
					    function beep() {
 | 
				
			||||||
        var audio = new Audio("{% static 'sound/beep.mp3' %}");
 | 
					        var audio = new Audio("{% static 'sound/beep.mp3' %}");
 | 
				
			||||||
            audio.volume = 0.1
 | 
					        audio.volume = 0.02;
 | 
				
			||||||
            console.log("Audio play {% static 'sound/beep.mp3' %}")
 | 
					        console.log("Audio play {% static 'sound/beep.mp3' %}");
 | 
				
			||||||
        audio.play();
 | 
					        audio.play();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function clearCanvas() {
 | 
				
			||||||
 | 
					        canvas = document.querySelector('#interactive .drawingBuffer');
 | 
				
			||||||
 | 
					        drawing = (canvas != null) ? canvas.getContext('2d') : null;
 | 
				
			||||||
 | 
					        console.log('Clearing canvas, drawing=' + drawing + ', canvas=' + canvas);
 | 
				
			||||||
 | 
					        if (drawing != null) {
 | 
				
			||||||
 | 
					            drawing.clearRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					            console.log('Clearing done');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log('detected HW concurrency: ' + self.navigator.hardwareConcurrency);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    window.addEventListener('load', function () {
 | 
					    window.addEventListener('load', function () {
 | 
				
			||||||
        let selectedDeviceId;
 | 
					        let selectedDeviceId;
 | 
				
			||||||
        let lastScanResult = '';
 | 
					        let lastScanResult = '';
 | 
				
			||||||
            // const codeReader = new ZXing.BrowserBarcodeReader()
 | 
					        let config = {
 | 
				
			||||||
            const codeReader = new ZXing.BrowserMultiFormatReader()
 | 
					            inputStream: {
 | 
				
			||||||
            console.log('ZXing code reader initialized')
 | 
					                name: "Live",
 | 
				
			||||||
            codeReader.getVideoInputDevices()
 | 
					                type: "LiveStream",
 | 
				
			||||||
                .then((videoInputDevices) => {
 | 
					                constraints: {
 | 
				
			||||||
                    const sourceSelect = document.getElementById('sourceSelect')
 | 
					                    facingMode: "environment",
 | 
				
			||||||
                    selectedDeviceId = videoInputDevices[0].deviceId
 | 
					                },
 | 
				
			||||||
                    if (videoInputDevices.length > 1) {
 | 
					 | 
				
			||||||
                        videoInputDevices.forEach((element) => {
 | 
					 | 
				
			||||||
                            const sourceOption = document.createElement('option')
 | 
					 | 
				
			||||||
                            sourceOption.text = element.label
 | 
					 | 
				
			||||||
                            sourceOption.value = element.deviceId
 | 
					 | 
				
			||||||
                            sourceSelect.appendChild(sourceOption)
 | 
					 | 
				
			||||||
                        })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        sourceSelect.onchange = () => {
 | 
					                target: document.getElementById('interactive'),
 | 
				
			||||||
                            selectedDeviceId = sourceSelect.value;
 | 
					                /*area: {
 | 
				
			||||||
 | 
					                    top: '0%',
 | 
				
			||||||
 | 
					                    bottom: '0%',
 | 
				
			||||||
 | 
					                    left: '0%',
 | 
				
			||||||
 | 
					                    right: '0%'
 | 
				
			||||||
 | 
					                },*/
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            //numOfWorkers: 4,
 | 
				
			||||||
 | 
					            locate: true,
 | 
				
			||||||
 | 
					            debug: true,
 | 
				
			||||||
 | 
					            frequency: 20,
 | 
				
			||||||
 | 
					            decoder: {
 | 
				
			||||||
 | 
					                readers: ["code_128_reader"],
 | 
				
			||||||
 | 
					                debug: {
 | 
				
			||||||
 | 
					                    drawScanline: false,
 | 
				
			||||||
 | 
					                    drawBoundingBox: false,
 | 
				
			||||||
 | 
					                    showPattern: false,
 | 
				
			||||||
 | 
					                    showFrequency: false
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                multiple: false
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            locator: {
 | 
				
			||||||
 | 
					                halfSample: false,
 | 
				
			||||||
 | 
					                patchSize: "x-large", // x-small, small, medium, large, x-large
 | 
				
			||||||
 | 
					                debug: {
 | 
				
			||||||
 | 
					                    /*showCanvas: true,*/
 | 
				
			||||||
 | 
					                    showPatches: false,
 | 
				
			||||||
 | 
					                    showFoundPatches: false,
 | 
				
			||||||
 | 
					                    showSkeleton: false,
 | 
				
			||||||
 | 
					                    showLabels: false,
 | 
				
			||||||
 | 
					                    showPatchLabels: false,
 | 
				
			||||||
 | 
					                    showRemainingPatchLabels: false,
 | 
				
			||||||
 | 
					                    boxFromPatches: {
 | 
				
			||||||
 | 
					                      showTransformed: false,
 | 
				
			||||||
 | 
					                      showTransformedBox: false,
 | 
				
			||||||
 | 
					                      showBB: true
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                        const sourceSelectPanel = document.getElementById('sourceSelectPanel')
 | 
					 | 
				
			||||||
                        sourceSelectPanel.style.display = 'block'
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    document.getElementById('startButton').addEventListener('click', () => {
 | 
					        function quagga_init() {
 | 
				
			||||||
                        beep()
 | 
					            Quagga.init(config, function(err) {
 | 
				
			||||||
                        codeReader.decodeFromInputVideoDeviceContinuously(selectedDeviceId, 'video', (result) => {
 | 
					                if (err) {
 | 
				
			||||||
                            if (result && lastScanResult !== result.text) {
 | 
					                    console.log(err);
 | 
				
			||||||
                                lastScanResult = result.text
 | 
					                    return;
 | 
				
			||||||
                                console.log('Got scan (cont.): ' + result)
 | 
					                }
 | 
				
			||||||
                                document.getElementById('result').textContent = result.text
 | 
					                console.log("Initialization finished. Ready to start");
 | 
				
			||||||
                                beep()
 | 
					                Quagga.start();
 | 
				
			||||||
                                useScannedText(result.text)
 | 
					                Quagga.onDetected(function(data) {
 | 
				
			||||||
                            } else if (result) {
 | 
					                    code = data.codeResult.code;
 | 
				
			||||||
                                console.log('... scan again')
 | 
					                    console.log('Got scan: ' + code);
 | 
				
			||||||
 | 
					                    if (code && lastScanResult !== code) {
 | 
				
			||||||
 | 
					                        lastScanResult = code;
 | 
				
			||||||
 | 
					                        console.log('Got scan (cont.): ' + lastScanResult);
 | 
				
			||||||
 | 
					                        document.querySelector('#result').textContent = lastScanResult;
 | 
				
			||||||
 | 
					                        // document.getElementById('result').textContent = lastScanResult
 | 
				
			||||||
 | 
					                        beep();
 | 
				
			||||||
 | 
					                        useScannedText(lastScanResult);
 | 
				
			||||||
 | 
					                        console.log(data);
 | 
				
			||||||
 | 
					                        canvas = document.querySelector('#interactive .drawingBuffer');
 | 
				
			||||||
 | 
					                        drawing = canvas.getContext('2d');
 | 
				
			||||||
 | 
					                        drawing.clearRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
 | 
					                        drawing.beginPath();
 | 
				
			||||||
 | 
					                        drawing.moveTo(data.line[0].x, data.line[0].y);
 | 
				
			||||||
 | 
					                        drawing.lineTo(data.line[1].x, data.line[1].y);
 | 
				
			||||||
 | 
					                        drawing.lineWidth = 2;
 | 
				
			||||||
 | 
					                        drawing.strokeStyle = 'yellow';
 | 
				
			||||||
 | 
					                        drawing.closePath();
 | 
				
			||||||
 | 
					                        drawing.stroke();
 | 
				
			||||||
 | 
					                        setTimeout(clearCanvas, 3000);
 | 
				
			||||||
 | 
					                    } else if (code) {
 | 
				
			||||||
 | 
					                        console.log('... scan again');
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					 | 
				
			||||||
                /*
 | 
					                /*
 | 
				
			||||||
                        codeReader.decodeOnceFromVideoDevice(selectedDeviceId, 'video').then((result) => {
 | 
					                Quagga.onProcessed(function(data) {
 | 
				
			||||||
                            if (lastScanResult !== result.text) {
 | 
					                    canvas = document.querySelector('#interactive .drawingBuffer');
 | 
				
			||||||
                                lastScanResult = result.text
 | 
					                    if (canvas != null) {
 | 
				
			||||||
                                console.log('Got scan: ' + result.text)
 | 
					                        context = canvas.getContext('2d');
 | 
				
			||||||
                                document.getElementById('result').textContent = result.text
 | 
					                        if (context) {
 | 
				
			||||||
                                useScannedText(result.text)
 | 
					                            context.clearRect(0, 0, canvas.width, canvas.height);
 | 
				
			||||||
                            } else {
 | 
					 | 
				
			||||||
                                console.log('... scan again')
 | 
					 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        }).catch((err) => {
 | 
					                    }
 | 
				
			||||||
                            console.error(err)
 | 
					                });*/
 | 
				
			||||||
                            document.getElementById('result').textContent = err
 | 
					            });
 | 
				
			||||||
                        })
 | 
					        }
 | 
				
			||||||
                        */
 | 
					
 | 
				
			||||||
                        console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
 | 
					        quagga_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
 | 
				
			||||||
 | 
					          console.log("enumerateDevices() not supported.");
 | 
				
			||||||
 | 
					          return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Quagga.CameraAccess.enumerateVideoDevices()
 | 
				
			||||||
 | 
					            .then(function(devices) {
 | 
				
			||||||
 | 
					                const sourceSelect = document.getElementById('sourceSelect');
 | 
				
			||||||
 | 
					                selectedDeviceId = devices[0].deviceId; // default
 | 
				
			||||||
 | 
					                if (devices.length > 1) {
 | 
				
			||||||
 | 
					                    devices.forEach(function(device) {
 | 
				
			||||||
 | 
					                        console.log(device.kind + ": " + device.label + " id=" + device.deviceId);
 | 
				
			||||||
 | 
					                        const sourceOption = document.createElement('option');
 | 
				
			||||||
 | 
					                        sourceOption.text = device.label;
 | 
				
			||||||
 | 
					                        sourceOption.value = device.deviceId;
 | 
				
			||||||
 | 
					                        sourceSelect.appendChild(sourceOption);
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    sourceSelect.onchange = () => {
 | 
				
			||||||
 | 
					                        selectedDeviceId = sourceSelect.value;
 | 
				
			||||||
 | 
					                        config.inputStream.constraints.deviceId = selectedDeviceId;
 | 
				
			||||||
 | 
					                        Quagga.stop();
 | 
				
			||||||
 | 
					                        quagga_init();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    const sourceSelectPanel = document.getElementById('sourceSelectPanel');
 | 
				
			||||||
 | 
					                    sourceSelectPanel.style.display = 'block';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                document.getElementById('startButton').addEventListener('click', () => {
 | 
				
			||||||
 | 
					                    beep();
 | 
				
			||||||
 | 
					                    Quagga.start();
 | 
				
			||||||
 | 
					                    console.log(`Started scan from camera with id ${selectedDeviceId}`)
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                document.getElementById('resetButton').addEventListener('click', () => {
 | 
					                document.getElementById('resetButton').addEventListener('click', () => {
 | 
				
			||||||
                    document.getElementById('result').textContent = '';
 | 
					                    document.getElementById('result').textContent = '';
 | 
				
			||||||
                        codeReader.reset();
 | 
					                    Quagga.stop();
 | 
				
			||||||
                        console.log('Reset.')
 | 
					                    console.log('Reset/stopped.')
 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                })
 | 
					                document.getElementById('clearOverlayButton').addEventListener('click', () => {
 | 
				
			||||||
                .catch((err) => {
 | 
					                    clearCanvas();
 | 
				
			||||||
                    console.error(err)
 | 
					 | 
				
			||||||
                })
 | 
					                })
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(function(err) {
 | 
				
			||||||
 | 
					              console.log(err);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					 | 
				
			||||||
<hr />
 | 
					<hr />
 | 
				
			||||||
<div class="row mb-3">
 | 
					<div class="row mb-3">
 | 
				
			||||||
    <div class="col-8">
 | 
					    <div class="col-8">
 | 
				
			||||||
@@ -145,14 +250,22 @@
 | 
				
			|||||||
    var containerIsValid = false;
 | 
					    var containerIsValid = false;
 | 
				
			||||||
    var assetIsValid = false;
 | 
					    var assetIsValid = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async function getObjectInfo(url) {
 | 
				
			||||||
 | 
					        const response = await fetch(url);
 | 
				
			||||||
 | 
					        const object_json = response.json();
 | 
				
			||||||
 | 
					        return object_json;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function getContainerInfo(containerId) {
 | 
					    async function getContainerInfo(containerId) {
 | 
				
			||||||
        document.querySelector('#container_id').value = containerId;
 | 
					        document.querySelector('#container_id').value = containerId;
 | 
				
			||||||
        url = '/api/containers?named_id=' + containerId;
 | 
					        url = '/api/containers?named_id=' + containerId;
 | 
				
			||||||
        document.querySelector('#container_details').value = 'Trying ' + url;
 | 
					        document.querySelector('#container_details').value = 'Trying ' + url;
 | 
				
			||||||
        const response = await fetch(url);
 | 
					        const container = await getObjectInfo(url);
 | 
				
			||||||
        const container = await response.json();
 | 
					 | 
				
			||||||
        if (container.count >= 1) {
 | 
					        if (container.count >= 1) {
 | 
				
			||||||
            document.querySelector('#container_details').value = container.results[0].description;
 | 
					            c = container.results[0];
 | 
				
			||||||
 | 
					            console.log(c);
 | 
				
			||||||
 | 
					            containerType = await getObjectInfo(c.container_type);
 | 
				
			||||||
 | 
					            document.querySelector('#container_details').value = c.description + "\n" + c.named_id + ': ' + c.color + "\n" + containerType.description;
 | 
				
			||||||
            containerIsValid = true;
 | 
					            containerIsValid = true;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            document.querySelector('#container_details').value = document.querySelector('#container_details').value + "\nERROR: No result\n";
 | 
					            document.querySelector('#container_details').value = document.querySelector('#container_details').value + "\nERROR: No result\n";
 | 
				
			||||||
@@ -178,15 +291,11 @@
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function useScannedText(msg) {
 | 
					    function useScannedText(msg) {
 | 
				
			||||||
        // const data = JSON.parse(e.data);
 | 
					 | 
				
			||||||
        // msg = String(data.message);
 | 
					 | 
				
			||||||
        document.querySelector('#chat-log').value += (msg + '\n');
 | 
					        document.querySelector('#chat-log').value += (msg + '\n');
 | 
				
			||||||
        if (msg.startsWith('C-')) {
 | 
					        if (msg.startsWith('C-')) {
 | 
				
			||||||
            getContainerInfo(data.message);
 | 
					            getContainerInfo(msg);
 | 
				
			||||||
            // document.querySelector('#container_id').value = msg;
 | 
					 | 
				
			||||||
        } else if (msg.startsWith('A-')) {
 | 
					        } else if (msg.startsWith('A-')) {
 | 
				
			||||||
            getAssetInfo(data.message);
 | 
					            getAssetInfo(msg);
 | 
				
			||||||
            // document.querySelector('#asset_id').value = msg;
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            assetId = 'A-' + msg;
 | 
					            assetId = 'A-' + msg;
 | 
				
			||||||
            document.querySelector('#asset_id').value = assetId;
 | 
					            document.querySelector('#asset_id').value = assetId;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,5 +6,7 @@ app_name = 'booker'
 | 
				
			|||||||
urlpatterns = [
 | 
					urlpatterns = [
 | 
				
			||||||
    path('', views.IndexView.as_view(), name='index'),
 | 
					    path('', views.IndexView.as_view(), name='index'),
 | 
				
			||||||
    path('smartphone_scanner/', views.SmartphoneScannerView.as_view(), name='smartphone_scanner'),
 | 
					    path('smartphone_scanner/', views.SmartphoneScannerView.as_view(), name='smartphone_scanner'),
 | 
				
			||||||
    path('<str:scanner_id>/', views.scanner, name='scanner')
 | 
					    path('chrome_scanner/', views.ChromeScannerView.as_view(), name='chrome_scanner'),
 | 
				
			||||||
 | 
					    path('<str:scanner_id>/', views.scanner_view, name='scanner'),
 | 
				
			||||||
 | 
					    path('webscanner/<str:scanner_id>/', views.web_scanner_view, name='web_scanner'),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,18 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					from channels.layers import get_channel_layer
 | 
				
			||||||
from django.shortcuts import render
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					from django.views.decorators.csrf import csrf_exempt
 | 
				
			||||||
from django.views.generic import ListView, TemplateView
 | 
					from django.views.generic import ListView, TemplateView
 | 
				
			||||||
 | 
					from gmqtt import Client
 | 
				
			||||||
 | 
					from rest_framework.utils import json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from homelog import settings
 | 
				
			||||||
 | 
					from homelog.system_user import get_system_user
 | 
				
			||||||
from .models import Scanner
 | 
					from .models import Scanner
 | 
				
			||||||
 | 
					from django.http import HttpResponse
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOGGER = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IndexView(ListView):
 | 
					class IndexView(ListView):
 | 
				
			||||||
@@ -8,10 +20,53 @@ class IndexView(ListView):
 | 
				
			|||||||
    template_name = 'booker/index.html'
 | 
					    template_name = 'booker/index.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def scanner(request, scanner_id):
 | 
					def scanner_view(request, scanner_id):
 | 
				
			||||||
    return render(request, 'booker/scanner.html', {
 | 
					    return render(request, 'booker/scanner.html', {
 | 
				
			||||||
        'scanner_id': scanner_id
 | 
					        'scanner_id': scanner_id
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SmartphoneScannerView(TemplateView):
 | 
					class SmartphoneScannerView(TemplateView):
 | 
				
			||||||
    template_name = 'booker/smartphone_scanner.html'
 | 
					    template_name = 'booker/smartphone_scanner.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChromeScannerView(TemplateView):
 | 
				
			||||||
 | 
					    template_name = 'booker/chrome_scanner.html'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@csrf_exempt
 | 
				
			||||||
 | 
					def web_scanner_view(request, scanner_id):
 | 
				
			||||||
 | 
					    def get_or_create_scanner(named_id):
 | 
				
			||||||
 | 
					        scanner, created = Scanner.objects.get_or_create(named_id=named_id, defaults={
 | 
				
			||||||
 | 
					            # 'lwt_topic': topic,
 | 
				
			||||||
 | 
					            'description': 'WebAPI scanner',
 | 
				
			||||||
 | 
					            'last_online_ts': datetime.now(),
 | 
				
			||||||
 | 
					            'created_by': get_system_user(),
 | 
				
			||||||
 | 
					            'changed_by': get_system_user()
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        if created:
 | 
				
			||||||
 | 
					            LOGGER.debug(f"Created new scanner entry for {named_id}")
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            LOGGER.debug(f"Updated scanner entry for {named_id}")
 | 
				
			||||||
 | 
					        return scanner
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f"Got {request.POST['scan']} from scanner {scanner_id}")
 | 
				
			||||||
 | 
					    get_or_create_scanner(scanner_id)
 | 
				
			||||||
 | 
					    topic = f"barcodescanner/{scanner_id}/scan"
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    client = Client('homelog-webscanner')
 | 
				
			||||||
 | 
					    client.set_auth_credentials(username=settings.MQTT_USER, password=settings.MQTT_PASSWORD)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    client.connect(settings.MQTT_HOST, 1883, keepalive=60, version=settings.MQTT_VERSION)
 | 
				
			||||||
 | 
					    client.publish(topic, request.POST['scan'])
 | 
				
			||||||
 | 
					    client.disconnect()
 | 
				
			||||||
 | 
					    # MqttConsumer().mqtt_publish({'topic': topic, 'payload': request.POST['scan'], 'retain': False})
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    channel_layer = get_channel_layer()
 | 
				
			||||||
 | 
					    channel_layer.group_send(
 | 
				
			||||||
 | 
					        'mqtt', # scanner_id,
 | 
				
			||||||
 | 
					        {"type": "distribute", "text": json.dumps({'message': request.POST['scan'], 'topic': topic})}
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    '''
 | 
				
			||||||
 | 
					    return HttpResponse(f"<html><body><p>OK, got {request.POST['scan']} from {scanner_id}</p></body></html>")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user