Browse Source

Clean up scanimation, add pen test script, update pipfile, level the scanned images

assignment
mt 3 years ago
parent
commit
ce649d30b1
  1. 3
      Pipfile
  2. 116
      Pipfile.lock
  3. 6
      config.yml
  4. 26
      scanimation/interfaces/backend/amazon.svg
  5. BIN
      scanimation/interfaces/backend/font/BebasNeue-Regular.eot
  6. BIN
      scanimation/interfaces/backend/font/BebasNeue-Regular.otf
  7. BIN
      scanimation/interfaces/backend/font/BebasNeue-Regular.ttf
  8. BIN
      scanimation/interfaces/backend/font/BebasNeue-Regular.woff
  9. BIN
      scanimation/interfaces/backend/font/BebasNeue-Regular.woff2
  10. BIN
      scanimation/interfaces/backend/font/DroidSansFallbackFull.ttf
  11. BIN
      scanimation/interfaces/backend/font/FreeSans.ttf
  12. 33
      scanimation/interfaces/backend/index.html
  13. 12
      scanimation/interfaces/backend/script.js
  14. 110
      scanimation/interfaces/backend/style.css
  15. 3
      scanimation/interfaces/frames/.gitignore
  16. 3
      scanimation/interfaces/index.html
  17. 125
      scanimation/interfaces/worker_specs/dateformat.js
  18. BIN
      scanimation/interfaces/worker_specs/font/BebasNeue-Regular.eot
  19. BIN
      scanimation/interfaces/worker_specs/font/BebasNeue-Regular.otf
  20. BIN
      scanimation/interfaces/worker_specs/font/BebasNeue-Regular.ttf
  21. BIN
      scanimation/interfaces/worker_specs/font/BebasNeue-Regular.woff
  22. BIN
      scanimation/interfaces/worker_specs/font/BebasNeue-Regular.woff2
  23. BIN
      scanimation/interfaces/worker_specs/font/DroidSansFallbackFull.ttf
  24. BIN
      scanimation/interfaces/worker_specs/font/FreeSans.ttf
  25. 58
      scanimation/interfaces/worker_specs/index.html
  26. 1
      scanimation/interfaces/worker_specs/reconnecting-websocket.min.js
  27. 118
      scanimation/interfaces/worker_specs/script.js
  28. 93
      scanimation/interfaces/worker_specs/style.css
  29. 165
      sorteerhoed/central_management.py
  30. 40
      sorteerhoed/plotter.py
  31. 67
      test_drawing.svg
  32. 131
      test_pen.py
  33. 10
      www/index.html

3
Pipfile

@ -13,8 +13,9 @@ httpagentparser = "*" @@ -13,8 +13,9 @@ httpagentparser = "*"
geoip2 = "*"
ink-extensions = "*"
python-magic = "*"
Pillow = "*"
tqdm = "*"
serial = "*"
pillow = "*"
pyserial = "*"
[dev-packages]

116
Pipfile.lock generated

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "29b194a198b1d9a791ea020295d0ef6d5969969f9d606e2457444485e1bd6f7a"
"sha256": "3c0954c7917f567561faffcf18031aa0718c5144698d0de7022dd9ad9d49b1c4"
},
"pipfile-spec": 6,
"requires": {
@ -21,18 +21,18 @@ @@ -21,18 +21,18 @@
},
"boto3": {
"hashes": [
"sha256:5db4db12a017be2a0b07ec662584b7b9e8afa05894c8aaac145576a7c39a9886",
"sha256:7fb8bf70ff2403991c8ae7bc548333811be6e432c7665721364ea0c858eb824e"
"sha256:98fdd6fa754e17c0d9e87fbb464c43c7e72aaa6b4f78b418eba47b7d15deffee",
"sha256:fdaae8edd63ae114107d375862069d17de23e00489a65169b8141ddee6bdf78b"
],
"index": "pypi",
"version": "==1.10.41"
"version": "==1.10.48"
},
"botocore": {
"hashes": [
"sha256:5bfffa38ebba26ab462bb40e858702390fbe3ae2093a2177a8cde050ad6cb7e3",
"sha256:62ddff63be904781f97ced737836a66f5b72579af788c905cfdab32d2970e15e"
"sha256:29370f50af7870661609fbfbc4ed01ef2fd531b87b98729700526d1a4b3a2f89",
"sha256:7f60edf33c6f5b7c1c9b9377267bdc56495f52704607f713d4c3bd1d82a08334"
],
"version": "==1.13.41"
"version": "==1.13.48"
},
"certifi": {
"hashes": [
@ -72,11 +72,11 @@ @@ -72,11 +72,11 @@
},
"geoip2": {
"hashes": [
"sha256:a37ddac2d200ffb97c736da8b8ba9d5d8dc47da6ec0f162a461b681ecac53a14",
"sha256:f7ffe9d258e71a42cf622ce6350d976de1d0312b9f2fbce3975c7d838b57ecf0"
"sha256:5869e987bc54c0d707264fec4710661332cc38d2dca5a7f9bb5362d0308e2ce0",
"sha256:99ec12d2f1271a73a0a4a2b663fe6ce25fd02289c0a6bef05c0a1c3b30ee95a4"
],
"index": "pypi",
"version": "==2.9.0"
"version": "==3.0.0"
},
"httpagentparser": {
"hashes": [
@ -155,45 +155,37 @@ @@ -155,45 +155,37 @@
},
"maxminddb": {
"hashes": [
"sha256:449a1713d37320d777d0db286286ab22890f0a176492ecf3ad8d9319108f2f79"
"sha256:d0ce131d901eb11669996b49a59f410efd3da2c6dbe2c0094fe2fef8d85b6336"
],
"version": "==1.5.1"
"version": "==1.5.2"
},
"pillow": {
"hashes": [
"sha256:047d9473cf68af50ac85f8ee5d5f21a60f849bc17d348da7fc85711287a75031",
"sha256:0f66dc6c8a3cc319561a633b6aa82c44107f12594643efa37210d8c924fc1c71",
"sha256:12c9169c4e8fe0a7329e8658c7e488001f6b4c8e88740e76292c2b857af2e94c",
"sha256:248cffc168896982f125f5c13e9317c059f74fffdb4152893339f3be62a01340",
"sha256:27faf0552bf8c260a5cee21a76e031acaea68babb64daf7e8f2e2540745082aa",
"sha256:285edafad9bc60d96978ed24d77cdc0b91dace88e5da8c548ba5937c425bca8b",
"sha256:384b12c9aa8ef95558abdcb50aada56d74bc7cc131dd62d28c2d0e4d3aadd573",
"sha256:38950b3a707f6cef09cd3cbb142474357ad1a985ceb44d921bdf7b4647b3e13e",
"sha256:4aad1b88933fd6dc2846552b89ad0c74ddbba2f0884e2c162aa368374bf5abab",
"sha256:4ac6148008c169603070c092e81f88738f1a0c511e07bd2bb0f9ef542d375da9",
"sha256:4deb1d2a45861ae6f0b12ea0a786a03d19d29edcc7e05775b85ec2877cb54c5e",
"sha256:59aa2c124df72cc75ed72c8d6005c442d4685691a30c55321e00ed915ad1a291",
"sha256:5a47d2123a9ec86660fe0e8d0ebf0aa6bc6a17edc63f338b73ea20ba11713f12",
"sha256:5cc901c2ab9409b4b7ac7b5bcc3e86ac14548627062463da0af3b6b7c555a871",
"sha256:6c1db03e8dff7b9f955a0fb9907eb9ca5da75b5ce056c0c93d33100a35050281",
"sha256:7ce80c0a65a6ea90ef9c1f63c8593fcd2929448613fc8da0adf3e6bfad669d08",
"sha256:809c19241c14433c5d6135e1b6c72da4e3b56d5c865ad5736ab99af8896b8f41",
"sha256:83792cb4e0b5af480588601467c0764242b9a483caea71ef12d22a0d0d6bdce2",
"sha256:846fa202bd7ee0f6215c897a1d33238ef071b50766339186687bd9b7a6d26ac5",
"sha256:9f5529fc02009f96ba95bea48870173426879dc19eec49ca8e08cd63ecd82ddb",
"sha256:a423c2ea001c6265ed28700df056f75e26215fd28c001e93ef4380b0f05f9547",
"sha256:ac4428094b42907aba5879c7c000d01c8278d451a3b7cccd2103e21f6397ea75",
"sha256:b1ae48d87f10d1384e5beecd169c77502fcc04a2c00a4c02b85f0a94b419e5f9",
"sha256:bf4e972a88f8841d8fdc6db1a75e0f8d763e66e3754b03006cbc3854d89f1cb1",
"sha256:c6414f6aad598364aaf81068cabb077894eb88fed99c6a65e6e8217bab62ae7a",
"sha256:c710fcb7ee32f67baf25aa9ffede4795fd5d93b163ce95fdc724383e38c9df96",
"sha256:c7be4b8a09852291c3c48d3c25d1b876d2494a0a674980089ac9d5e0d78bd132",
"sha256:c9e5ffb910b14f090ac9c38599063e354887a5f6d7e6d26795e916b4514f2c1a",
"sha256:e0697b826da6c2472bb6488db4c0a7fa8af0d52fa08833ceb3681358914b14e5",
"sha256:e9a3edd5f714229d41057d56ac0f39ad9bdba6767e8c888c951869f0bdd129b0"
"sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
"sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946",
"sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837",
"sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f",
"sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00",
"sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d",
"sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533",
"sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a",
"sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358",
"sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda",
"sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435",
"sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2",
"sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313",
"sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff",
"sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317",
"sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2",
"sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614",
"sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0",
"sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386",
"sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9",
"sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636",
"sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"
],
"index": "pypi",
"version": "==6.2.1"
"version": "==7.0.0"
},
"pyserial": {
"hashes": [
@ -205,11 +197,11 @@ @@ -205,11 +197,11 @@
},
"python-dateutil": {
"hashes": [
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7'",
"version": "==2.8.0"
"version": "==2.8.1"
},
"python-magic": {
"hashes": [
@ -221,20 +213,20 @@ @@ -221,20 +213,20 @@
},
"pyyaml": {
"hashes": [
"sha256:0e7f69397d53155e55d10ff68fdfb2cf630a35e6daf65cf0bdeaf04f127c09dc",
"sha256:2e9f0b7c5914367b0916c3c104a024bb68f269a486b9d04a2e8ac6f6597b7803",
"sha256:35ace9b4147848cafac3db142795ee42deebe9d0dad885ce643928e88daebdcc",
"sha256:38a4f0d114101c58c0f3a88aeaa44d63efd588845c5a2df5290b73db8f246d15",
"sha256:483eb6a33b671408c8529106df3707270bfacb2447bf8ad856a4b4f57f6e3075",
"sha256:4b6be5edb9f6bb73680f5bf4ee08ff25416d1400fbd4535fe0069b2994da07cd",
"sha256:7f38e35c00e160db592091751d385cd7b3046d6d51f578b29943225178257b31",
"sha256:8100c896ecb361794d8bfdb9c11fce618c7cf83d624d73d5ab38aef3bc82d43f",
"sha256:c0ee8eca2c582d29c3c2ec6e2c4f703d1b7f1fb10bc72317355a746057e7346c",
"sha256:e4c015484ff0ff197564917b4b4246ca03f411b9bd7f16e02a2f586eb48b6d04",
"sha256:ebc4ed52dcc93eeebeae5cf5deb2ae4347b3a81c3fa12b0b8c976544829396a4"
"sha256:059b2ee3194d718896c0ad077dd8c043e5e909d9180f387ce42012662a4946d6",
"sha256:1cf708e2ac57f3aabc87405f04b86354f66799c8e62c28c5fc5f88b5521b2dbf",
"sha256:24521fa2890642614558b492b473bee0ac1f8057a7263156b02e8b14c88ce6f5",
"sha256:4fee71aa5bc6ed9d5f116327c04273e25ae31a3020386916905767ec4fc5317e",
"sha256:70024e02197337533eef7b85b068212420f950319cc8c580261963aefc75f811",
"sha256:74782fbd4d4f87ff04159e986886931456a1894c61229be9eaf4de6f6e44b99e",
"sha256:940532b111b1952befd7db542c370887a8611660d2b9becff75d39355303d82d",
"sha256:cb1f2f5e426dc9f07a7681419fe39cee823bb74f723f36f70399123f439e9b20",
"sha256:dbbb2379c19ed6042e8f11f2a2c66d39cceb8aeace421bfc29d085d93eda3689",
"sha256:e3a057b7a64f1222b56e47bcff5e4b94c4f61faac04c7c4ecb1985e18caa3994",
"sha256:e9f45bd5b92c7974e59bcd2dcc8631a6b6cc380a904725fce7bc08872e691615"
],
"index": "pypi",
"version": "==5.2"
"version": "==5.3"
},
"requests": {
"hashes": [
@ -285,6 +277,14 @@ @@ -285,6 +277,14 @@
"index": "pypi",
"version": "==6.0.3"
},
"tqdm": {
"hashes": [
"sha256:4789ccbb6fc122b5a6a85d512e4e41fc5acad77216533a6f2b8ce51e0f265c23",
"sha256:efab950cf7cc1e4d8ee50b2bb9c8e4a89f8307b49e0b2c9cfef3ec4ca26655eb"
],
"index": "pypi",
"version": "==4.41.1"
},
"urllib3": {
"hashes": [
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",

6
config.yml

@ -17,7 +17,7 @@ server: @@ -17,7 +17,7 @@ server:
port: 8888
scanner: # size of scanarea in mm
# total visible glass (and size of the scan)
width: 255
width: 255
height: 185
# area which can be removed
draw_width: 255
@ -25,3 +25,7 @@ scanner: # size of scanarea in mm @@ -25,3 +25,7 @@ scanner: # size of scanarea in mm
# part of scanner that is invissible left & top
left_padding: 0
top_padding: 45
level:
min: 0
max: 90
gamma: 0.9

26
scanimation/interfaces/backend/amazon.svg

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

BIN
scanimation/interfaces/backend/font/BebasNeue-Regular.eot

Binary file not shown.

BIN
scanimation/interfaces/backend/font/BebasNeue-Regular.otf

Binary file not shown.

BIN
scanimation/interfaces/backend/font/BebasNeue-Regular.ttf

Binary file not shown.

BIN
scanimation/interfaces/backend/font/BebasNeue-Regular.woff

Binary file not shown.

BIN
scanimation/interfaces/backend/font/BebasNeue-Regular.woff2

Binary file not shown.

BIN
scanimation/interfaces/backend/font/DroidSansFallbackFull.ttf

Binary file not shown.

BIN
scanimation/interfaces/backend/font/FreeSans.ttf

Binary file not shown.

33
scanimation/interfaces/backend/index.html

@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script src='/socket.io/socket.io.js'></script>
</head>
<body>
<div id="wrapper">
<div id="logo"> <img src="amazon.svg" /> </div>
<table>
<thead>
<tr>
<th>worker id</th>
<th>ip address</th>
<th>country</th>
<th>fee</th>
<th>task start time</th>
<th>task completion time</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<script src="script.js"></script>
</body>
</html>

12
scanimation/interfaces/backend/script.js

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
let list = document.querySelector("tbody")
let testdata = `<td>AMZ3976824398</td><td>234.183.281.221</td><td>united states</td><td>&euro;0.20</td><td>wed 30 oct 14:56</td><td>04m 37s</td>`
for(let i=0; i < 200; i++){
let entry = document.createElement('tr')
entry.innerHTML = testdata
list.append(entry)
}

110
scanimation/interfaces/backend/style.css

@ -1,110 +0,0 @@ @@ -1,110 +0,0 @@
@font-face {
font-family: 'bebas';
src: url('font/BebasNeue-Regular.ttf');
}
@font-face {
font-family: 'freesans';
src: url('font/FreeSans.ttf')
}
:root{
--base-font-size: 12px;
--spec_name-font-size: 120%;
--spec_value-font-size: 250%;
--base-color: #271601; /* tekst */
--alt-color: #FFF5DF; /* achtergrond */
--amazon-color: #F0C14C;
/* ////// GAT ACHTERKANT PLEK & POSITIE /////// */
/* */ /* */
/* */ --pos-x: 20px; /* */
/* */ --pos-y: 20px; /* */
/* */ --width: 90%; /* 115mm */
/* */ --height: 100%; /* 500mm */
/* */ /* */
/* //////////////////////////////////////////// */
}
html, body{
margin: 0;
padding: 0;
border: 0;
text-decoration: none;
font-family: 'freesans';
font-size: var(--base-font-size);
line-height: 1.1;
background: #555;
overflow: hidden;
}
#logo{
background: #555;
width: 100%;
padding: 2% 0% 1% 0%;
text-align: right;
}
#wrapper{
position: absolute;
left: var(--pos-x);
top: var(--pos-y);
width: var(--width);
/* height: var(--height); */
background: var(--alt-color);
box-sizing: border-box;
/* padding: 2%; */
}
table{
display: grid;
border-collapse: collapse;
min-width: 100%;
grid-template-columns: repeat(6, 1fr);
}
thead, tbody, tr{
display: contents;
}
th,
td {
padding: 2%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
th {
position: -webkit-sticky;
position: sticky;
background-image: linear-gradient(var(--alt-color), var(--amazon-color)) ;
top: 0;
text-align: left;
font-weight: normal;
font-size: 1.1rem;
color: var(--base-color);
}
th:last-child {
border: 0;
}
td {
padding-top: 2%;
padding-bottom: 2%;
color: #808080;
}
tr:nth-child(even) td {
background: #f8f6f9;
}

3
scanimation/interfaces/frames/.gitignore vendored

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
*
!.gitignore

3
scanimation/interfaces/index.html

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
<a href="worker_specs/index.html">worker specs</a><br />
<a href="animation/index.html">frame animation</a><br />
<a href="backend/index.html">backend</a>

125
scanimation/interfaces/worker_specs/dateformat.js

@ -1,125 +0,0 @@ @@ -1,125 +0,0 @@
/*
* Date Format 1.2.3
* (c) 2007-2009 Steven Levithan <stevenlevithan.com>
* MIT license
*
* Includes enhancements by Scott Trenda <scott.trenda.net>
* and Kris Kowal <cixar.com/~kris.kowal/>
*
* Accepts a date, a mask, or a date and a mask.
* Returns a formatted version of the given date.
* The date defaults to the current date/time.
* The mask defaults to dateFormat.masks.default.
*/
var dateFormat = function () {
var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
timezoneClip = /[^-+\dA-Z]/g,
pad = function (val, len) {
val = String(val);
len = len || 2;
while (val.length < len) val = "0" + val;
return val;
};
// Regexes and supporting functions are cached through closure
return function (date, mask, utc) {
var dF = dateFormat;
// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
mask = date;
date = undefined;
}
// Passing date through Date applies Date.parse, if necessary
date = date ? new Date(date) : new Date;
if (isNaN(date)) throw SyntaxError("invalid date");
mask = String(dF.masks[mask] || mask || dF.masks["default"]);
// Allow setting the utc argument via the mask
if (mask.slice(0, 4) == "UTC:") {
mask = mask.slice(4);
utc = true;
}
var _ = utc ? "getUTC" : "get",
d = date[_ + "Date"](),
D = date[_ + "Day"](),
m = date[_ + "Month"](),
y = date[_ + "FullYear"](),
H = date[_ + "Hours"](),
M = date[_ + "Minutes"](),
s = date[_ + "Seconds"](),
L = date[_ + "Milliseconds"](),
o = utc ? 0 : date.getTimezoneOffset(),
flags = {
d: d,
dd: pad(d),
ddd: dF.i18n.dayNames[D],
dddd: dF.i18n.dayNames[D + 7],
m: m + 1,
mm: pad(m + 1),
mmm: dF.i18n.monthNames[m],
mmmm: dF.i18n.monthNames[m + 12],
yy: String(y).slice(2),
yyyy: y,
h: H % 12 || 12,
hh: pad(H % 12 || 12),
H: H,
HH: pad(H),
M: M,
MM: pad(M),
s: s,
ss: pad(s),
l: pad(L, 3),
L: pad(L > 99 ? Math.round(L / 10) : L),
t: H < 12 ? "a" : "p",
tt: H < 12 ? "am" : "pm",
T: H < 12 ? "A" : "P",
TT: H < 12 ? "AM" : "PM",
Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
};
return mask.replace(token, function ($0) {
return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
});
};
}();
// Some common format strings
dateFormat.masks = {
"default": "ddd mmm dd yyyy HH:MM:ss",
shortDate: "m/d/yy",
mediumDate: "mmm d, yyyy",
longDate: "mmmm d, yyyy",
fullDate: "dddd, mmmm d, yyyy",
shortTime: "h:MM TT",
mediumTime: "h:MM:ss TT",
longTime: "h:MM:ss TT Z",
isoDate: "yyyy-mm-dd",
isoTime: "HH:MM:ss",
isoDateTime: "yyyy-mm-dd'T'HH:MM:ss",
isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};
// Internationalization strings
dateFormat.i18n = {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
]
};
// For convenience...
Date.prototype.format = function (mask, utc) {
return dateFormat(this, mask, utc);
};

BIN
scanimation/interfaces/worker_specs/font/BebasNeue-Regular.eot

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/BebasNeue-Regular.otf

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/BebasNeue-Regular.ttf

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/BebasNeue-Regular.woff

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/BebasNeue-Regular.woff2

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/DroidSansFallbackFull.ttf

Binary file not shown.

BIN
scanimation/interfaces/worker_specs/font/FreeSans.ttf

Binary file not shown.

58
scanimation/interfaces/worker_specs/index.html

@ -1,58 +0,0 @@ @@ -1,58 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="style.css" />
<script src='dateformat.js'></script>
<script src='reconnecting-websocket.min.js'></script>
</head>
<body>
<div id="wrapper">
<!-- <div class="phase" id="waiting_for_human">
<span class="narrative_phase_text">waiting for human worker to accept task</span>
</div>
<div class="phase" id="human_accepted_task">
<span class="narrative_phase_text">task accepted by human worker</span>
</div> -->
<div class="phase" id="worker_specs">
<span class="grid-item spec_name" id="hit_id_descriptor">human intelligent task id</span>
<span class="grid-item spec_value" id="hit_id">&nbsp;</span>
<span class="grid-item spec_name" id="worker_id_descriptor">human worker id</span>
<span class="grid-item spec_value" id="worker_id">&nbsp;</span>
<span class="grid-item spec_name" id="ip_descriptor">ip address</span>
<span class="grid-item spec_value" id="ip">&nbsp;</span>
<span class="grid-item spec_name" id="location_descriptor">location</span>
<span class="grid-item spec_value" id="location">&nbsp;</span>
<span class="grid-item spec_name" id="browser_descriptor">browser</span>
<span class="grid-item spec_value" id="browser">&nbsp;</span>
<span class="grid-item spec_name" id="os_descriptor">os</span>
<span class="grid-item spec_value" id="os">&nbsp;</span>
<span class="grid-item spec_name" id="hit_opened_descriptor">task started at</span>
<span class="grid-item spec_value" id="hit_opened">&nbsp;</span>
<span class="grid-item spec_name" id="state_descriptor">task status</span>
<span class="grid-item spec_value" id="state">&nbsp;</span>
<span class="grid-item spec_name" id="fee_descriptor">task fee</span>
<span class="grid-item spec_value" id="fee">&nbsp;</span>
<span class="grid-item spec_name" id="elapsed_time_descriptor">time elapsed</span>
<span class="grid-item spec_value" id="elapsed_time">&nbsp;</span>
</div>
</div>
<script src="script.js"></script>
</body>
</html>

1
scanimation/interfaces/worker_specs/reconnecting-websocket.min.js vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
!function(a,b){"function"==typeof define&&define.amd?define([],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.ReconnectingWebSocket=b()}(this,function(){function a(b,c,d){function l(a,b){var c=document.createEvent("CustomEvent");return c.initCustomEvent(a,!1,!1,b),c}var e={debug:!1,automaticOpen:!0,reconnectInterval:1e3,maxReconnectInterval:3e4,reconnectDecay:1.5,timeoutInterval:2e3};d||(d={});for(var f in e)this[f]="undefined"!=typeof d[f]?d[f]:e[f];this.url=b,this.reconnectAttempts=0,this.readyState=WebSocket.CONNECTING,this.protocol=null;var h,g=this,i=!1,j=!1,k=document.createElement("div");k.addEventListener("open",function(a){g.onopen(a)}),k.addEventListener("close",function(a){g.onclose(a)}),k.addEventListener("connecting",function(a){g.onconnecting(a)}),k.addEventListener("message",function(a){g.onmessage(a)}),k.addEventListener("error",function(a){g.onerror(a)}),this.addEventListener=k.addEventListener.bind(k),this.removeEventListener=k.removeEventListener.bind(k),this.dispatchEvent=k.dispatchEvent.bind(k),this.open=function(b){h=new WebSocket(g.url,c||[]),b||k.dispatchEvent(l("connecting")),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","attempt-connect",g.url);var d=h,e=setTimeout(function(){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","connection-timeout",g.url),j=!0,d.close(),j=!1},g.timeoutInterval);h.onopen=function(){clearTimeout(e),(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onopen",g.url),g.protocol=h.protocol,g.readyState=WebSocket.OPEN,g.reconnectAttempts=0;var d=l("open");d.isReconnect=b,b=!1,k.dispatchEvent(d)},h.onclose=function(c){if(clearTimeout(e),h=null,i)g.readyState=WebSocket.CLOSED,k.dispatchEvent(l("close"));else{g.readyState=WebSocket.CONNECTING;var d=l("connecting");d.code=c.code,d.reason=c.reason,d.wasClean=c.wasClean,k.dispatchEvent(d),b||j||((g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onclose",g.url),k.dispatchEvent(l("close")));var e=g.reconnectInterval*Math.pow(g.reconnectDecay,g.reconnectAttempts);setTimeout(function(){g.reconnectAttempts++,g.open(!0)},e>g.maxReconnectInterval?g.maxReconnectInterval:e)}},h.onmessage=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onmessage",g.url,b.data);var c=l("message");c.data=b.data,k.dispatchEvent(c)},h.onerror=function(b){(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","onerror",g.url,b),k.dispatchEvent(l("error"))}},1==this.automaticOpen&&this.open(!1),this.send=function(b){if(h)return(g.debug||a.debugAll)&&console.debug("ReconnectingWebSocket","send",g.url,b),h.send(b);throw"INVALID_STATE_ERR : Pausing to reconnect websocket"},this.close=function(a,b){"undefined"==typeof a&&(a=1e3),i=!0,h&&h.close(a,b)},this.refresh=function(){h&&h.close()}}return a.prototype.onopen=function(){},a.prototype.onclose=function(){},a.prototype.onconnecting=function(){},a.prototype.onmessage=function(){},a.prototype.onerror=function(){},a.debugAll=!1,a.CONNECTING=WebSocket.CONNECTING,a.OPEN=WebSocket.OPEN,a.CLOSING=WebSocket.CLOSING,a.CLOSED=WebSocket.CLOSED,a});

118
scanimation/interfaces/worker_specs/script.js

@ -1,118 +0,0 @@ @@ -1,118 +0,0 @@
// DOM STUFF ///////////////////////////////////////////////////////////////////
let divs = {},
spec_names = [
'worker_id',
'ip',
'location',
'browser',
'os',
'state',
'fee',
'hit_created',
'hit_opened',
'hit_submitted',
'elapsed_time',
'hit_id'
]
divs.linkDOM = function(name){
divs[name] = document.getElementById(`${name}`)
}
spec_names.forEach(function(name){
divs.linkDOM(name)
})
let request_time = timeStamp(),
hit_started = false,
elapsed_time,
hit_finished = false
// SOCKET STUFF ////////////////////////////////////////////////////////////////
let ws = new ReconnectingWebSocket('ws://localhost:8888/status/ws')
ws.addEventListener('open', () => {
// ws.send('hi server')
})
ws.addEventListener('message', (event) => {
console.log('message: ' + event.data)
let data = JSON.parse(event.data)
if(data.property === 'hit_opened') {
if(data.value != null){
hit_started = true
hit_finished = false
request_time = new Date()
divs[data.property].innerHTML = `${request_time.format('dd mmm HH:MM:ss')}`
}else{
divs[data.property].innerHTML = `&mdash;`
hit_started = false
}
}
else if(data.property === 'hit_submitted'){
hit_finished = true;
}
else if(divs[data.property]){
data.value === null ? divs[data.property].innerHTML = `&mdash;` : divs[data.property].innerHTML = `${data.value}`
}
})
// ANIMATION STUFF /////////////////////////////////////////////////////////////
let frames,
frames_per_sec = 10,
current_frame = 0
function makeAnimation(){
let now,
delta = 0,
last = timeStamp(),
step = 1/frames_per_sec
function frame() {
now = timeStamp()
delta += Math.min(1, (now - last) / 1000)
while(delta > step){
delta -= step
update(step)
}
last = now
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
}
function update(step){
if(!hit_finished) elapsed_time = `${new Date((Date.now() - request_time)).format('MM"m "ss"s"')}`
if(hit_started){
divs['elapsed_time'].innerHTML = elapsed_time
}else{
divs['elapsed_time'].innerHTML = `&mdash;`
}
}
makeAnimation()
function timeStamp(){return window.performance && window.performance.now ? window.performance.now() : new Date().getTime()}

93
scanimation/interfaces/worker_specs/style.css

@ -1,93 +0,0 @@ @@ -1,93 +0,0 @@
@font-face {
font-family: 'bebas';
src: url('font/BebasNeue-Regular.ttf');
}
@font-face {
font-family: 'freesans';
src: url('font/FreeSans.ttf')
}
:root{
--base-font-size: 22px;
--spec_name-font-size: 120%;
--spec_value-font-size: 250%;
--base-color: #271601; /* tekst */
--alt-color: #FFF5DF; /* achtergrond */
/* /////// GAT VOORKANT PLEK & POSITIE //////// */
/* */ /* */
/* */ --pos-x: 555px; /* */
/* */ --pos-y: 120px; /* */
/* */ --width: 420px; /* 115mm */
/* */ --height: 1000px; /* 270mm */
/* */ /* */
/* //////////////////////////////////////////// */
}
html, body{
margin: 0;
padding: 0;
border: 0;
text-decoration: none;
font-family: 'bebas';
font-size: var(--base-font-size);
line-height: 1.1;
background: var(--alt-color);
overflow: hidden;
}
#wrapper{
position: absolute;
left: var(--pos-x);
top: var(--pos-y);
width: var(--width);
height: var(--height);
background: var(--alt-color);
box-sizing: border-box;
padding: 2%;
}
#worker_specs{
display:grid;
grid-template-columns: 1fr ;
grid-template-rows: repeat(50, 1fr 2fr);
}
.grid-item{
color: var(--base-color);
}
.spec_name{
font-size: var(--spec_name-font-size);
font-family: 'freesans';
}
.spec_value{
font-size: var(--spec_value-font-size);
padding-bottom: 2%;
}
.phase{
display:none;
}
.phase:not(#worker_specs){
padding-top: 100%;
text-align: center;
}
.narrative_phase_text{
font-size: var(--spec_value-font-size);
}

165
sorteerhoed/central_management.py

@ -16,12 +16,52 @@ import io @@ -16,12 +16,52 @@ import io
from PIL import Image
import datetime
from shutil import copyfile
import colorsys
class Level(object):
# Level effect adapted from https://stackoverflow.com/a/3125421
def __init__(self, minv, maxv, gamma):
self.minv= minv/255.0
self.maxv= maxv/255.0
self._interval= self.maxv - self.minv
self._invgamma= 1.0/gamma
def new_level(self, value):
if value <= self.minv: return 0.0
if value >= self.maxv: return 1.0
return ((value - self.minv)/self._interval)**self._invgamma
def convert_and_level(self, band_values):
h, s, v= colorsys.rgb_to_hsv(*(i/255.0 for i in band_values))
new_v= self.new_level(v)
return tuple(int(255*i)
for i
in colorsys.hsv_to_rgb(h, s, new_v))
@classmethod
def level_image(cls, image, minv=0, maxv=255, gamma=1.0):
"""Level the brightness of image (a PIL.Image instance)
All values minv will become 0
All values maxv will become 255
gamma controls the curve for all values between minv and maxv"""
if image.mode != "RGB":
raise ValueError("this works with RGB images only")
new_image= image.copy()
leveller= cls(minv, maxv, gamma)
levelled_data= [
leveller.convert_and_level(data)
for data in image.getdata()]
new_image.putdata(levelled_data)
return new_image
class CentralManagement():
"""
Central management reads config and controls process flow
The HIT Store is the archive of hits
mturk thread communicates with mturk
server thread is tornado communicating with the turkers and with the status interface on the installation
@ -34,26 +74,26 @@ class CentralManagement(): @@ -34,26 +74,26 @@ class CentralManagement():
self.debug = debug_mode
self.currentHit = None
self.lastHitTime = None
self.eventQueue = Queue()
self.isRunning = threading.Event()
self.isScanning = threading.Event()
self.scanLock = threading.Lock()
self.notPaused = threading.Event()
def loadConfig(self, filename, args):
self.configFile = filename
self.args = args
self.reloadConfig()
varDb = os.path.join(
# self.config['storage_dir'],
'hit_store.db'
)
)
self.store = HITStore.Store(varDb, logLevel=logging.DEBUG if self.debug else logging.INFO)
self.logger.debug(f"Loaded configuration: {self.config}")
def reloadConfig(self):
@ -61,19 +101,19 @@ class CentralManagement(): @@ -61,19 +101,19 @@ class CentralManagement():
with open(self.configFile, 'r') as fp:
self.logger.debug('Load config from {}'.format(self.configFile))
self.config = yaml.safe_load(fp)
if self.args.no_plotter:
self.config['dummy_plotter'] = True
self.config['for_real'] = self.args.for_real
# self.panopticon = Panopticon(self, self.config, self.voiceStorage)
def start(self):
self.isRunning.set()
self.notPaused.set()
try:
# M-turk connection
MTURK_SANDBOX = 'https://mturk-requester-sandbox.us-east-1.amazonaws.com'
MTURK_REAL = 'https://mturk-requester.us-east-1.amazonaws.com'
@ -84,12 +124,12 @@ class CentralManagement(): @@ -84,12 +124,12 @@ class CentralManagement():
region_name='us-east-1',
endpoint_url = MTURK_REAL if self.config['for_real'] else MTURK_SANDBOX
)
self.logger.info(f"Mechanical turk account balance: {self.mturk.get_account_balance()['AvailableBalance']}")
if not self.config['for_real']:
self.logger.info("Remove block from sandbox worker account")
self.mturk.delete_worker_block(WorkerId='A1CK46PK9VEUH5', Reason='Myself on Sandbox')
# clear any pending hits:
pending_hits = self.mturk.list_hits(MaxResults=100)
for pending_hit in pending_hits['HITs']:
@ -101,29 +141,29 @@ class CentralManagement(): @@ -101,29 +141,29 @@ class CentralManagement():
ExpireAt=datetime.datetime.fromisoformat('2015-01-01')
)
self.mturk.delete_hit(HITId=pending_hit['HITId'])
self.sqs = SqsListener(self.config, self.eventQueue, self.isRunning)
sqsThread = threading.Thread(target=self.sqs.start, name='sqs')
sqsThread.start()
# the plotter itself
self.plotter = Plotter(self.config, self.eventQueue, self.isRunning, self.scanLock)
plotterThread = threading.Thread(target=self.plotter.start, name='plotter')
plotterThread.start()
# webserver for turks and status
self.server = Server(self.config, self.eventQueue, self.isRunning, self.plotter.q, self.store)
serverThread = threading.Thread(target=self.server.start, name='server')
serverThread.start()
# event listener:
dispatcherThread = threading.Thread(target=self.eventListener, name='dispatcher')
dispatcherThread.start()
#
#
#
#
self.eventQueue.put(Signal('start', {'ding':'test'}))
while self.isRunning.is_set():
time.sleep(.5)
except Exception as e:
@ -132,9 +172,9 @@ class CentralManagement(): @@ -132,9 +172,9 @@ class CentralManagement():
self.logger.warning("Stopping Central Managment")
self.isRunning.clear()
self.server.stop()
self.expireCurrentHit()
def expireCurrentHit(self):
if self.currentHit and self.currentHit.hit_id: # hit pending
self.logger.warn(f"Delete hit: {self.currentHit.hit_id}")
@ -163,7 +203,7 @@ class CentralManagement(): @@ -163,7 +203,7 @@ class CentralManagement():
- scan complete
- HIT created
- Plotter complete
-
-
"""
#TODO: make level debug()
self.logger.info(f"SIGNAL: {signal}")
@ -199,7 +239,7 @@ class CentralManagement(): @@ -199,7 +239,7 @@ class CentralManagement():
self.currentHit.turk_ip = value
if name == 'location':
self.currentHit.turk_country = value
self.logger.debug(f'Set status: {name} to {value}')
self.server.statusPage.set(name, value)
elif signal.name == 'server.open':
@ -214,7 +254,7 @@ class CentralManagement(): @@ -214,7 +254,7 @@ class CentralManagement():
self.plotter.park()
self.server.statusPage.set('hit_opened', self.currentHit.open_page_at)
# park always triggers a plotter.finished after being processed
elif signal.name[:4] == 'sqs.':
if signal.params['event']['HITId'] != self.currentHit.hit_id:
self.logger.warning(f"SQS hit.info hit_id != currenthit.id: {signal}, update status for older HIT")
@ -223,7 +263,7 @@ class CentralManagement(): @@ -223,7 +263,7 @@ class CentralManagement():
else:
sqsHit = self.currentHit
updateStatus = True
if signal.name == 'sqs.AssignmentAccepted':
self.logger.info(f'Set status progress to accepted')
sqsHit.accept_time = datetime.datetime.strptime(signal.params['event']['EventTimestamp'],"%Y-%m-%dT%H:%M:%SZ")
@ -244,7 +284,7 @@ class CentralManagement(): @@ -244,7 +284,7 @@ class CentralManagement():
if updateStatus:
self.setLight(False)
self.mturk.create_worker_block(WorkerId=signal.params['event']['WorkerId'], Reason='Accepted task without working on it.')
elif signal.name == 'sqs.AssignmentReturned':
self.logger.info(f'Set status progress to returned')
sqsHit.accept_time = None
@ -264,7 +304,7 @@ class CentralManagement(): @@ -264,7 +304,7 @@ class CentralManagement():
sqsHit.answer = signal.params['event']['Answer']
if sqsHit.uuid not in sqsHit.answer:
self.logger.critical(f"Not a valid answer given?! {sqsHit.answer}")
if not sqsHit.submit_page_at:
# page not submitted, hit is. Nevertheless, create new hit.
try:
@ -274,14 +314,14 @@ class CentralManagement(): @@ -274,14 +314,14 @@ class CentralManagement():
self.makeHit()
else:
sqsHit.submit_hit_at = datetime.datetime.strptime(signal.params['event']['EventTimestamp'],"%Y-%m-%dT%H:%M:%SZ")
# Merijn: hier block ik de worker na succesvolle submit, om dubbele workers te voorkomen
# Disabled after worker mail, use quals instead
# block de worker na succesvolle submit, om dubbele workers te voorkomen
# TODO: Disabled after worker mail, use quals instead
#self.mturk.create_worker_block(WorkerId=signal.params['event']['WorkerId'], Reason='Every worker can only work once on the taks.')
#self.logger.warn("Block worker after submission")
self.store.saveHIT(sqsHit)
if updateStatus:
self.logger.warning(f'update status: {sqsHit.getStatus()}')
# TODO: have HITStore/HIT take care of this by emitting a signal
@ -301,21 +341,21 @@ class CentralManagement(): @@ -301,21 +341,21 @@ class CentralManagement():
except Exception as e:
self.logger.critical(f"Exception on handling {signal}")
self.logger.exception(e)
def makeHit(self):
self.expireCurrentHit() # expire hit if it is there
self.server.statusPage.reset()
self.reloadConfig() # reload new config values if they are set
# self.notPaused.wait()
self.currentHit = self.store.createHIT()
self.store.currentHit = self.currentHit
self.logger.info(f"Make HIT {self.currentHit.id}")
# question = open(self.config['amazon']['task_xml'], mode='r').read().replace("{HIT_NR}",str(self.currentHit.id))
question = '''<?xml version="1.0" encoding="UTF-8"?>
<ExternalQuestion xmlns="http://mechanicalturk.amazonaws.com/AWSMechanicalTurkDataSchemas/2006-07-14/ExternalQuestion.xsd">
@ -324,8 +364,8 @@ class CentralManagement(): @@ -324,8 +364,8 @@ class CentralManagement():
</ExternalQuestion>
'''.replace("{HIT_NR}",str(self.currentHit.id))
estimatedHitDuration = self.store.getEstimatedHitDuration()
# set minimum rate, if they take longer we increase the pay
fee = max(self.config['minimum_fee'], (self.config['hour_rate_aim']/3600.) * estimatedHitDuration)
self.currentHit.fee = fee
@ -341,22 +381,22 @@ class CentralManagement(): @@ -341,22 +381,22 @@ class CentralManagement():
AutoApprovalDelayInSeconds = self.config['hit_autoapprove_delay'],
Question = question,
)
self.logger.info(f"Created hit: {new_hit}")
if not self.config['for_real']:
self.logger.info("https://workersandbox.mturk.com/mturk/preview?groupId=" + new_hit['HIT']['HITGroupId'])
else:
self.logger.info("https://worker.mturk.com/mturk/preview?groupId=" + new_hit['HIT']['HITGroupId'])
self.currentHit.hit_id = new_hit['HIT']['HITId']
self.store.saveHIT(self.currentHit)
# TODO: have HITStore/HIT take care of this by emitting a signal
self.server.statusPage.set('hit_id', new_hit['HIT']['HITId'])
self.server.statusPage.set('hit_created', self.currentHit.created_at)
self.server.statusPage.set('fee', f"${self.currentHit.fee:.2f}")
self.server.statusPage.set('state', self.currentHit.getStatus())
# mturk.send_test_event_notification()
if self.config['amazon']['sqs_url']:
notification_info= self.mturk.update_notification_settings(
@ -383,7 +423,7 @@ class CentralManagement(): @@ -383,7 +423,7 @@ class CentralManagement():
Active=True
)
self.logger.debug(notification_info)
def cleanDrawing(self):
with self.scanLock:
self.eventQueue.put(Signal('scan.start'))
@ -401,33 +441,33 @@ class CentralManagement(): @@ -401,33 +441,33 @@ class CentralManagement():
time.sleep(5) # sleep a few seconds for scanner to return to start position
if e:
self.logger.critical(f"Scanner caused: {e.decode()}")
self.eventQueue.put(Signal('system.reset'))
self.eventQueue.put(Signal('scan.finished'))
def reset(self) -> str:
self.plotter.park()
# Very confusing to have scanning on a reset (because often nothing has happened), so don't do itd
# scan = threading.Thread(target=self.cleanDrawing, name='reset')
# scan.start()
self.server.statusPage.clearAssignment()
def scanImage(self) -> str:
"""
Run scanimage on scaner and returns a string with the filename
"""
cmd = [
'sudo', 'scanimage', '-d', 'epkowa', '--format', 'jpeg',
'sudo', 'scanimage', '-d', 'epkowa', '--format', 'tiff',
'--resolution=100', # lower res, faster (more powerful) scan & wipe
'-l','25' #y axis, margin from top of the scanner, hence increasing this, moves the scanned image upwards
,'-t','22', # x axis, margin from left side scanner (seen from the outside)
'-x',str(181),
'-y',str(245)
'-x',str(181),
'-y',str(245)
]
self.logger.info(f"{cmd}")
filename = self.currentHit.getImagePath()
with self.scanLock:
self.eventQueue.put(Signal('scan.start'))
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -436,23 +476,24 @@ class CentralManagement(): @@ -436,23 +476,24 @@ class CentralManagement():
if e:
self.logger.critical(f"Scanner caused: {e.decode()}")
#TODO: should clear self.isRunning.clear() ?
try:
f = io.BytesIO(o)
img = Image.open(f)
img = img.transpose(Image.ROTATE_90).transpose(Image.FLIP_TOP_BOTTOM)
img.save(filename)
tunedImg = Level.level_image(img, self.config['level']['min'], self.config['level']['max'], self.config['level']['gamma'])
tunedImg.save(filename)
except Exception as e:
self.logger.critical("Cannot create image from scan. Did scanner work?")
self.logger.exception(e)
# TODO: create
# TODO: create
copyfile('www/basic.svg', filename)
time.sleep(5) # sleep a few seconds for scanner to return to start position
self.eventQueue.put(Signal('hit.scanned', {'hit_id':self.currentHit.id}))
self.eventQueue.put(Signal('scan.finished'))
def setLight(self, on):
value = 1 if on else 0
cmd = [
@ -462,5 +503,3 @@ class CentralManagement(): @@ -462,5 +503,3 @@ class CentralManagement():
code = subprocess.call(cmd)
if code > 0:
self.logger.warning(f"Error on light change: {code}")

40
sorteerhoed/plotter.py

@ -15,7 +15,7 @@ class Plotter: @@ -15,7 +15,7 @@ class Plotter:
self.isRunning = runningEvent
self.logger = logging.getLogger("sorteerhoed").getChild("plotter")
self.pen_down = False
self.plotWidth = self.config['scanner']['width'] / 10 / 2.54
self.plotHeight = self.config['scanner']['height'] / 10 / 2.54
self.xPadding = self.config['scanner']['left_padding'] / 10 / 2.54;
@ -25,7 +25,7 @@ class Plotter: @@ -25,7 +25,7 @@ class Plotter:
self.goPark = False
self.locked = False
self.ad = None
def park(self):
self.logger.info("Queue to park plotter")
self.goPark = True
@ -33,31 +33,30 @@ class Plotter: @@ -33,31 +33,30 @@ class Plotter:
absPlotWidth = 26.5/2.54
topLeft = absPlotWidth / self.plotWidth #ignore changes in config plotwidth
self.q.put([(1/2.54)/absPlotWidth + topLeft,0,0])
def start(self):
try:
if not self.config['dummy_plotter']:
self.ad = axidraw.AxiDraw()
self.ad.interactive()
# self.ad.plot_path()
connected = self.ad.connect()
if not connected:
raise Exception("Cannot connect to Axidraw")
# self.ad.options.units = 1 # set to use centimeters instead of inches
self.ad.options.accel = 100;
self.ad.options.speed_penup = 100
self.ad.options.speed_pendown = 100
self.ad.options.model = 1 # 2 for A3, 1 for A4
self.ad.options.pen_pos_up = 100
self.park()
# self.ad.moveto(0,0)
else:
self.ad = None
self.axiDrawCueListener()
@ -73,17 +72,17 @@ class Plotter: @@ -73,17 +72,17 @@ class Plotter:
except Exception as e:
self.logger.warning("Error on closing axidraw:")
self.logger.exception(e)
self.logger.info("Clear running Event")
# send shutdown signal (if not already set)
self.isRunning.clear()
def draw_segments(self, segments = []):
if not self.locked:
# acquire lock if not already done so
self.scannerLock.acquire()
self.locked = True
coordinates = []
for segment in segments:
coordinate = [
@ -97,8 +96,8 @@ class Plotter: @@ -97,8 +96,8 @@ class Plotter:
if coordinate[0] < self.xPadding or coordinate[0] > self.xPadding+self.plotWidth or \
coordinate[1] < self.yPadding or coordinate[1] > self.yPadding + self.plotHeight:
self.logger.warn(f"Skip drawing for: {coordinates} out of bounds")
continue
continue
coordinates.append(coordinate)
self.logger.debug(f"Plot: {coordinates}")
if self.ad:
@ -110,7 +109,7 @@ class Plotter: @@ -110,7 +109,7 @@ class Plotter:
# self.ad.moveto(move[0]* plotterWidth, move[1]*plotterHeight)
# self.logger.debug(f'handler! {move}')
pass
def setPenDown(self, pen_state):
"""
False: pen raised, True: pen_lower
@ -140,20 +139,20 @@ class Plotter: @@ -140,20 +139,20 @@ class Plotter:
#if self.goPark:
# print("seg",segment)
# change of pen state? draw previous segments!
if (segment[2] == 1 and not self.pen_down) or (segment[2] == 0 and self.pen_down) or len(segments) > 150:
if len(segments):
self.draw_segments(segments)
plotterRan = True
segments = [] #reset