This commit is contained in:
fencl
2025-11-04 13:50:38 +01:00
commit 024eb52a69
23 changed files with 2407 additions and 0 deletions

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
# Python
__pycache__
*.pyc
*.pyo
# .idea
*.iws
**/.idea/workspace.xml **/.idea/tasks.xml
# virtual environment
/venv/

30
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,30 @@
stages:
- buildimage
- checks
- tests
- deploytest
variables:
APP_IMAGE: gitlab.princip.cz:4567/$CI_PROJECT_PATH/app-$CI_COMMIT_REF_NAME
Build Application (docker) Image:
stage: buildimage
image: docker:git
before_script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN gitlab.princip.cz:4567
script:
- docker build -t $APP_IMAGE .
- docker push $APP_IMAGE
except:
- tags
Static Checks:
stage: checks
image: $APP_IMAGE
before_script:
- pip install flake8
script:
- make pep8

16
Dockerfile Normal file
View File

@@ -0,0 +1,16 @@
FROM python:3.9
LABEL maintainer="Martin Fencl <martin.fencl@eurowag.com>"
RUN apt-get update -y && \
apt-get dist-upgrade -y
WORKDIR /app
COPY requirements.txt ./
RUN pip3 install --upgrade pip && \
pip3 install -Ur requirements.txt -i https://pypi.princip.cz
COPY . .
CMD [ "python", "./app.py" ]

44
Makefile Normal file
View File

@@ -0,0 +1,44 @@
# Makefile
help:
@echo "Please use 'make <target>' where <target> is one of"
@echo " clean clean up __pycache__"
@echo " clean-build clean build files and directories"
@echo " mrproper all cleaning jobs"
@echo " pep8 to check PEP 8"
@echo " static-check to check PEP 8 and merge markers"
clean:
@echo "-------------------"
@echo "Purifying 'pycache'"
@echo "-------------------"
rm -fv **/*.pyc
rm -fv **/*.pyo
clean-build:
@echo "--------------"
@echo "Cleaning build"
@echo "--------------"
rm -rfv *.egg-info/
rm -rfv dist/
mrproper: clean clean-build
# pep8:
# @echo "--------------"
# @echo "Checking PEP"
# @echo "--------------"
# @echo "**pycodestyle**"
# pycodestyle .
#
# @echo "--------------"
# @echo "**flake8**"
# flake8
pep8:
@echo "--------------"
@echo "**flake8**"
@echo "--------------"
flake8 app.py --exclude=migrations
flake8 scripts --exclude=migrations
# static-check: pep8 merge-markers

30
README.md Normal file
View File

@@ -0,0 +1,30 @@
# SWMU web
### Swmu project support web tool
See: https://gitlab.princip.cz/lab/sw/uhura/swmu/-/wikis/home
<pre>
_____ _ ____ ____ _ _
/ ___|| | | | \/ | | | | | |
\ `--. | | | | . . | | | |_ _____| |__
`--. \| |/\| | |\/| | | | \ \ /\ / / _ \ '_ \
/\__/ /\ /\ / | | | |_| |\ V V / __/ |_) |
\____/ \/ \/\_| |_/\___/ \_/\_/ \___|_.__/
O O
\\ //
\\ //
\\ //
/~~~~~\
,-------------------,
| ,---------------, |
| | | |
| | | |
| | | |
| | | |
| |_______________| |
|___________________|
|___________________|
</pre>

440
app.py Normal file
View File

@@ -0,0 +1,440 @@
import os
import shutil
import folium
import pandas as pd
from flask import Flask
from flask import request, render_template
from flask import send_from_directory, url_for, redirect
from werkzeug.utils import secure_filename
from scripts import create_mulog
print("version: 1.5")
ASSETS_DIR = os.path.dirname(os.path.abspath(__file__))
app = Flask(__name__)
# app.config['SECRET_KEY'] = '^%huYtFd90;90jjj'
# app.config['MULOGS'] = 'static'
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 10 # 10mb
app.config['MAX_RECOMMENDED_POS'] = 10000
app.config['UPLOAD_EXTENSIONS'] = ['.log']
app.config['UPLOAD_PATH'] = 'uploads'
app.config['FILE_NAME'] = app.config['IGN1A'] = None
questions = []
counter_files = 0
@app.route('/', methods=['GET', 'POST'])
@app.route("/home", methods=['GET', 'POST'])
def index():
environment = request.host
clear = False
if request.method == 'POST':
if len(questions) > 20:
del questions[:1]
if request.form['submit_button'] == "Clear":
del questions[:]
return render_template('index_v2.html', questions=questions,
environment=environment, clear=True,
lenquestions=len(questions))
elif request.form['submit_button'] == "Generate":
if request.form['modes'] == "None":
questions.append({
'loglevel': request.form['loglevels'],
'id32': request.form['start-id32'],
'number_swmu': request.form['number_of_swmu'],
'mode': "None", # set default run mode
'server_host': request.form['server_hosts'],
'server_port': request.form['server_ports'],
'start_port': request.form['start_ports'],
'bt_spontaneous_timeout': request.form[
'bt_spontaneous_timeouts'],
'sigma': request.form['sigmas'],
'sta': request.form['stas'],
'sta_pause': request.form['sta_pauses'],
'ft_delay': request.form['ft_delays'],
'ft_mode': request.form['ft_modes'],
})
elif request.form['modes'] == "run":
questions.append({
'loglevel': request.form['loglevels'],
'id32': request.form['start-id32'],
'number_swmu': request.form[
'number_of_swmu'],
'mode': request.form['modes'],
'server_host': request.form['server_hosts'],
'server_port': request.form['server_ports'],
'start_port': request.form['start_ports'],
'bt_spontaneous_timeout': request.form[
'bt_spontaneous_timeouts'],
'sigma': request.form['sigmas'],
'sta': request.form['stas'],
'sta_pause': request.form['sta_pauses'],
'ft_delay': request.form['ft_delays'],
'ft_mode': request.form['ft_modes'],
'latitude': request.form['latitudes'],
'longitude': request.form['longitudes'],
'domain': request.form['domains'],
'near_by_domain': request.form['near_by_domains'],
'x01': request.form['x01s'],
'x01_pause': request.form['x01_pauses'],
'units_wo_x1a': request.form['units_wo_x1as'],
'x1a_pause': request.form['x1a_pauses'],
'x1a_mask': request.form['x1a_masks'],
'x1a_type': request.form['x1a_types'],
'x1d': request.form['x1ds'],
'x1d_pause': request.form['x1d_pauses'],
'x1d_weight_axles': request.form['x1d_weight_axles'],
})
elif request.form['modes'] == "sim":
questions.append({
'loglevel': request.form['loglevels'],
'id32': request.form['start-id32'],
'number_swmu': request.form[
'number_of_swmu'],
'mode': request.form['modes'],
'server_host': request.form['server_hosts'],
'server_port': request.form['server_ports'],
'start_port': request.form['start_ports'],
'bt_spontaneous_timeout': request.form[
'bt_spontaneous_timeouts'],
'sigma': request.form['sigmas'],
'sta': request.form['stas'],
'sta_pause': request.form['sta_pauses'],
'ft_delay': request.form['ft_delays'],
'ft_mode': request.form['ft_modes'],
'log': request.form['logs'],
'sim_time_check': request.form['sim_time_checks'],
'sim_stop': request.form['sim_stops'],
})
elif request.form['modes'] == "simfast":
questions.append({
'loglevel': request.form['loglevels'],
'id32': request.form['start-id32'],
'number_swmu': request.form[
'number_of_swmu'],
'mode': request.form['modes'],
'server_host': request.form['server_hosts'],
'server_port': request.form['server_ports'],
'start_port': request.form['start_ports'],
'bt_spontaneous_timeout': request.form[
'bt_spontaneous_timeouts'],
'sigma': request.form['sigmas'],
'sta': request.form['stas'],
'sta_pause': request.form['sta_pauses'],
'ft_delay': request.form['ft_delays'],
'ft_mode': request.form['ft_modes'],
'log': request.form['logs_simfast'],
'sim_time_check': request.form['sim_time_checks_simfast'],
'sim_stop': request.form['sim_stops'],
'simulate_pause': request.form['simulate_pauses'],
})
return render_template('index_v2.html', questions=questions,
environment=environment, clear=clear,
lenquestions=len(questions))
@app.route('/info')
def info():
environment = request.host
return render_template('info.html', environment=environment)
@app.route('/helpswmu')
def helpswmu():
environment = request.host
return render_template('helpswmu.html', environment=environment)
"""
@app.route('/mulog_read')
def mulog_read():
environment = request.host
return render_template('mulog_read.html', environment=environment)
"""
@app.route('/mulog_read')
def upload_file():
environment = request.host
# environment_road = f"http://{environment}/uploader" # on local
environment_road = f"https://{environment}/uploader"
max_pos = app.config['MAX_RECOMMENDED_POS']
files = listdir_nopos(app.config['UPLOAD_PATH'])
max_size = (app.config['MAX_CONTENT_LENGTH']) / (1024 * 1024) # in mb
ign1a = app.config['IGN1A']
text = warn = ""
mulogt = []
other_type_ttt = []
pos_file = {'latitude': [], 'longitude': [], "msg_type": []}
x01 = x04 = x05 = x08 = x09 = x13 = x14 = x25 = x26 = x0b = 0
x0d = x1a = x1c = x1d = x02 = x10 = x0e = ts_c = pos_c = 0
if files:
try:
mulog_path = f"{app.config['UPLOAD_PATH']}/{files[0]}"
mulog_data = create_mulog.dir_mulog(
import_dir=mulog_path, sim_time_check=False)
# (pos, log, ts_, type, pos)
for (count, message, old_ts, message_type, message_type_parse,
message_type_full_parse, list_ts_warning) in mulog_data:
warn = list_ts_warning
if message_type["type"] == b'\x01':
x01 += 1
elif message_type["type"] == b'\x04':
x04 += 1
elif message_type["type"] == b'\x05':
x05 += 1
elif message_type["type"] == b'\x08':
x08 += 1
elif message_type["type"] == b'\x09':
x09 += 1
elif message_type["type"] == b'\x13':
x13 += 1
elif message_type["type"] == b'\x14':
x14 += 1
elif message_type["type"] == b'\x25':
x25 += 1
elif message_type["type"] == b'\x26':
x26 += 1
elif message_type["type"] == b'\x0b':
x0b += 1
elif message_type["type"] == b'\x0d':
x0d += 1
elif message_type["type"] == b'\x1a':
x1a += 1
if ign1a == "False":
keys = []
key_fast = message_type_full_parse["fastlog"]
for key in key_fast.keys():
if key == "latitude":
keys.append(key)
elif key == "longitude":
keys.append(key)
if "latitude" and "longitude" in keys:
key_lat = key_fast["latitude"]
key_lon = key_fast["longitude"]
for lat in key_lat:
pos_file["latitude"].append(lat)
for lon in key_lon:
pos_file["longitude"].append(lon)
msg_type = message_type_parse["type"]
pos_file["msg_type"].append(msg_type)
pos_c += 1
elif message_type["type"] == b'\x1c':
x1c += 1
elif message_type["type"] == b'\x1d':
x1d += 1
elif message_type["type"] == b'\x02':
x02 += 1
elif message_type["type"] == b'\x10':
x10 += 1
elif message_type["type"] == b'\n':
continue
elif message_type["type"] == b'"':
continue
elif message_type["type"] == b'#':
continue
elif message_type["type"] == b'\x0e':
x0e += 1
else:
other_type_ttt.append(message_type["type"])
if old_ts is not None:
ts_c += 1
if message_type["type"] != b'\x1a':
if "latitude" in message_type_parse and \
"longitude" in message_type_parse:
lat = message_type_parse["latitude"]
lon = message_type_parse["longitude"]
msg_type = message_type_parse["type"]
pos_file["latitude"].append(lat)
pos_file["longitude"].append(lon)
pos_file["msg_type"].append(msg_type)
pos_c += 1
if not other_type_ttt:
other_type_ttt.append("None")
if pos_file is not []:
header_key = ["latitude", "longitude", "msg_type"]
df = pd.DataFrame(pos_file)
df.to_csv(f"{app.config['UPLOAD_PATH']}"
f"/{files[0]}_pos_file.csv", columns=header_key)
mulogt.extend([x01, x04, x05, x08, x09, x13, x14, x25,
x26, x0b, x0d, x1a, x1c, x1d, x10,
x0e, other_type_ttt])
except IOError:
print("Error: File does not appear to exist.")
# remove()
return "Error: File does not appear to exist.", 400
except ValueError:
remove()
text = f"ValueError: {ValueError}, file deleted"
print(text)
pass
return render_template('mulog_read.html', files=files,
max_size=max_size, environment=environment,
environment_road=environment_road, mulog=mulogt,
ts_c=ts_c, pos_c=pos_c, text=text, warn=warn,
ign1a=ign1a, max_pos=max_pos,
_scheme="https", _external=True)
# don't use use_scheme and _external with local testing
@app.route('/uploader', methods=['GET', 'POST'])
def uploader_file():
if request.method == 'POST':
app.config['IGN1A'] = request.form['ign1a']
uploaded_file = request.files['file']
filename = secure_filename(uploaded_file.filename)
app.config['FILE_NAME'] = filename
if filename != '':
file_ext = os.path.splitext(filename)[1]
if file_ext not in app.config['UPLOAD_EXTENSIONS']:
return "Invalid file, supports only <b>.log</b> format", 400
remove()
uploaded_file.save(
os.path.join(app.config['UPLOAD_PATH'], filename))
return redirect(
url_for('upload_file', _scheme="https", _external=True))
# don't use use_scheme and _external with local testing
@app.route('/uploads/<filename>')
def upload(filename):
return send_from_directory(app.config['UPLOAD_PATH'], filename)
@app.route('/call_remove')
def call_remove():
remove()
environment = request.host
# environment_road = f"http://{environment}/uploader" # on local
environment_road = f"https://{environment}/uploader"
files = os.listdir(app.config['UPLOAD_PATH'])
max_size = (app.config['MAX_CONTENT_LENGTH']) / (1024 * 1024) # in mb
return render_template('mulog_read.html', files=files,
max_size=max_size, environment=environment,
environment_road=environment_road, mulog=[],
_scheme="https", _external=True)
# don't use use_scheme and _external with local testing
@app.route('/call_render')
def call_render():
file_path = f"{app.config['UPLOAD_PATH']}/" \
f"{app.config['FILE_NAME']}_pos_file.csv"
file_ = pd.read_csv(file_path)
file_use = file_[["latitude", "longitude"]]
if file_use.empty:
return f"Failed to render {file_path}, file positions empty.", 400
else:
map_ = folium.Map(location=[file_use.latitude.mean(),
file_use.longitude.mean()],
zoom_start=14, control_scale=True)
i_count = 0
for _, row in file_use.iterrows():
if row['latitude'] == "latitude":
continue
else:
i_count += 1
lat_lon = f"{i_count}\n{row['latitude']}\n{row['longitude']}"
if i_count == 1:
folium.Marker([row['latitude'], row['longitude']],
popup=f"First position: \n{lat_lon}",
icon=folium.Icon(color="green",
icon="fas fa-truck"),
).add_to(map_)
elif i_count == len(file_use):
folium.Marker([row['latitude'], row['longitude']],
popup=f"Last position \n{lat_lon}",
icon=folium.Icon(color="red",
icon="fas fa-truck"),
).add_to(map_)
else:
folium.Marker([row['latitude'], row['longitude']],
popup=f"{lat_lon}\n",
).add_to(map_)
"""
points = []
points.append(tuple([row['latitude'], row['longitude']]))
folium.PolyLine(points, color="red",weight=2.5, opacity=1).add_to(map_)
"""
return map_._repr_html_() # https://fontawesome.com/
@app.errorhandler(413)
def too_large(e):
return f"File is too large {e}, maximum is " \
f"{(app.config['MAX_CONTENT_LENGTH']) / (1024 * 1024)}</b>mb", 413
@app.errorhandler(404)
def not_found(e):
return f"Error: File does not appear to exist, {e}.", 404
@app.errorhandler(500)
def some_error(e):
return f"{e}. Try <b>Clear</b> button", 500
def remove():
print("deleted")
folder = app.config['UPLOAD_PATH']
for filename in os.listdir(folder):
file_path = os.path.join(folder, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
return f"Failed to delete %s. Reason: {(file_path, e)}", 400
def listdir_nopos(path):
r = []
for _ in os.listdir(path):
if not _.endswith('_pos_file.csv'):
r.append(_)
return r
"""
@app.route('/SomeFunction')
def SomeFunction():
print('In SomeFunction')
return "Nothing"
@app.route('/clear')
def SomeFunction2():
print('In SomeFunction2')
return "Nothing"
@app.route('/swmu_run')
def swmu_run():
os.system(
return "Nothing"
"""
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=50002)
"""
app.run(debug=True, host="0.0.0.0", port=5000 2, ssl_context=(
"server.crt", "server.key"
))
"""
"""
app.run(debug=True, host="0.0.0.0", port=50002,
ssl_context=('cert/cert.pem', 'cert/key.pem')
)
"""

9
docker-compose.yml Normal file
View File

@@ -0,0 +1,9 @@
version: "3.9"
services:
swmu:
image: registry.martinfencl.eu/swmu_web:ver1.3
ports:
- 8046:50002

148
help.md Normal file
View File

@@ -0,0 +1,148 @@
****SWMU arguments (when entering the command runswmu -h):****
**mandatory arguments:**
-- UNITS unit amount
--mode MODE mode run | sim | simfast
**optional arguments for "run" mode:**
*--latitude LATITUDE*
latitude (default 47.687622035085916)
*--longitude LONGITUDE*
longitude (default 17.744894027593666)
*--domain DOMAIN*
current domain (default HU00)
*--near_by_domain NEAR_BY_DOMAIN*
near by domain (default HU00)
*--x01 GENERATE_X01*
generate x01 (default True)
*--x01-pause X01_PAUSE*
x01 pause in seconds (default 5sec)
*--units-wo-x1a UNITS_WO_X1A*
amount of units that will not generate x1a (default 0)
*--x1a-pause X1A_PAUSE*
x1a pause in seconds (default 10sec)
*--x1a-mask X1A_MASK*
x1a mask-type of log:
- 01 - without Regain signal flag;
- 17 - with Regain signal flag (default);
*--x1a-type X1A_TYPE*
x1a type of log (number of positions):
- 0 - (default);
- 1 - 17pos;
- 2 - 33pos;
*--x1d GENERATE_X1D*
generate x1d (default True)
*--x1d-pause X1D_PAUSE*
x1d pause in seconds (default 60sec)
*--x1d-weight_axles X1D_WEIGHT_AXLES*
x1d weight + number of axles:
- 'auto' - generate 2-10 (default);
- '42' - set weight 4, axle 2;
- '76' - set weight 7, axle 6;
- '22' - set weight 2, axle 2;
- ..
**optional arguments for "sim" and "simfast" mode:**
--log IMPORT_DIR
path to simulated log (obligatory with "sim" mode)
--simulate_pause SIMULATE_PAUSE
used for mode "simfast" to set delay between each message in seconds (default 2)
**shared arguments:**
*-h, --help*
show this help message and exit
*--server-host SERVER_HOST*
server host (default localhost)
*--server-port SERVER_PORT*
server port (default 31001)
*--start-id32 START_ID32*
start id32 (default 3567779840)
*--start-port START_PORT*
start port (default 1024)
*--bt-spontaneous-timeout BT_SPONTANEOUS_TIMEOUT*
BT spontaneous timeout in seconds (default 20)
*--sigma SIGMA*
sigma in seconds (default 0.2)
*--sta SEND_STA*
send sta (default True)
*--sta-pause STA_PAUSE*
STA pause in seconds (default 30sec)
****SWMU examples:****
Example for 5 Software Mobile Units and server host `localhost`:
```bash
$ runswmu 5 --mode run
```
```bash
$ runswmu 5 --mode sim --log ./test_log.log
```
```bash
$ runswmu 5 --mode simfast --log ./test_log.log --simulate_pause 0.25
```
**Other examples**:
```bash
$ runswmu 1 --mode run --server-host localhost --start-id32 3567779841
```
```bash
$ runswmu 1 --mode sim --server-host localhost --start-id32 3567779841 --log ./test_log.log
```
```bash
$ runswmu 1 --mode run --server-host 10.48.172.135 --start-id32 3567779841 --bt-spontaneous-timeout 5 --simulate_pause 0.25
```
```bash
$ LOG_LEVEL="INFO" runswmu 5 --mode sim --server-host localhost --start-id32 3567779871 --bt-spontaneous-timeout 5 --simulate_pause 0.25 --log ./test_log.log
```
```bash
$ LOG_LEVEL="INFO" runswmu 2500 --mode run --server-host 10.48.172.135 --bt-spontaneous-timeout 20 --sigma 3 --sta True --sta-pause 60 --x01-pause 11 --x1d-pause 250 --x1a-pause 18 --x1a-type 1 --domain DE00 --near_by_domain CZ00 --start-id32 3567779841
```
```bash
$ runswmu 1 --mode run --server-host 10.48.172.135 --start-id32 3567779841 --bt-spontaneous-timeout 5 --simulate_pause 0.25
```
```bash
$ runswmu 50 --mode simfast --server-host localhost --start-id32 3567779841 --log ./test_log.log --simulate_pause 0.25
```
**how to create pod (k8s):**
```bash
$ kubectl [-n namespace] run [name_of_pod] --rm -i --tty --image [name_of_image] -- /bin/bash
```
```bash
$ kubectl -n uhura-azure-wagexp run swmu --namespace=uhura-azure-wagexp --image=python --requests=cpu=100m,memory=512Mi --limits=cpu=200m,memory=2048Mi --rm -i --tty -- /bin/bash
```
```bash
$ kubectl -n uhura-azure-wagexp run swmu --rm -i --tty --image python -- /bin/bash
```
**how to add mulog_file to k8s pod**:
```bash
$ kubectl [-n namespace] cp [path to mulog file] [pod_name]:/
```
```bash
$ kubectl -n uhura-azure-wagexp cp /home/directory/test_log.log swmu:/
```

10
requirements.txt Normal file
View File

@@ -0,0 +1,10 @@
Flask
# colorlog
crc16
folium
Werkzeug
pandas
# requests
# our packages
muidconv
directive

56
scripts/create_mulog.py Normal file
View File

@@ -0,0 +1,56 @@
import logging
import time
from scripts import utils
from directive.munet.log_record import identify, parse, full_parse
# from .settings import conf
LOGGER = logging.LoggerAdapter(
logging.getLogger("swmu"), {"unitid": "unassigned"})
def load_log(import_dir, sim_time_check):
with open(import_dir) as fin:
# take only message even when row is f"{log_address}: {message}"
fin_data = [row.split(":")[-1].strip() for row in fin]
index = 0
list_ts = []
list_ts_warning = []
five_year = time.time() - 157784630
for message in fin_data:
message_ts = utils.get_timestamp_of_message(
bytes.fromhex(message))
if sim_time_check == "max_y" and message_ts is not None:
if message_ts < five_year:
list_ts_warning.append(
(index, message, message_ts))
continue
message_type = identify(bytes.fromhex(message))
try:
message_type_parse = parse(bytes.fromhex(message))
message_type_full_parse = full_parse(bytes.fromhex(message))
except Exception:
# except Exception as e:
# logging.warning(e)
continue
if message_ts is not None and message_ts <= 423792000:
list_ts_warning.append(
(index, message, message_ts))
list_ts.append(
(index, message, message_ts, message_type, message_type_parse,
message_type_full_parse, list_ts_warning))
index += 1
"""
if list_ts_warning:
LOGGER.warning(
f"timestamp in mulog is suspicious ([row in mulog]"
f", [message], [timestamp]): {list_ts_warning}")
"""
return list_ts
def dir_mulog(import_dir, sim_time_check):
mulog_data = load_log(import_dir, sim_time_check)
return mulog_data

139
scripts/utils.py Normal file
View File

@@ -0,0 +1,139 @@
CHARTOITA2 = {
"A": int("11000", 2),
"B": int("10011", 2),
"C": int("01110", 2),
"D": int("10010", 2),
"E": int("10000", 2),
"F": int("10110", 2),
"G": int("01011", 2),
"H": int("00101", 2),
"I": int("01100", 2),
"J": int("11010", 2),
"K": int("11110", 2),
"L": int("01001", 2),
"M": int("00111", 2),
"N": int("00110", 2),
"O": int("00011", 2),
"P": int("01101", 2),
"Q": int("11101", 2),
"R": int("01010", 2),
"S": int("10100", 2),
"T": int("00001", 2),
"U": int("11100", 2),
"V": int("01111", 2),
"W": int("11001", 2),
"X": int("10111", 2),
"Y": int("10101", 2),
"Z": int("10001", 2),
"<CR>": int("00010", 2),
"<LF>": int("01000", 2),
"<LS>": int("11111", 2),
"<FS>": int("11011", 2),
"<SP>": int("00100", 2),
"<NULL>": int("00000", 2),
}
# message type: offset (bytes)
MESSAGE_TYPE_TIMESTAMP_OFFSET = {
0x01: 8,
0x02: 4,
0x04: 8,
0x10: 4,
0x14: 4,
0x1a: 4,
0x1d: 4,
0x23: 4,
0x0b85: 4,
0x1c01: 4,
0x1c02: 4,
}
def bytes2uint(input_bytes):
return int.from_bytes(input_bytes, byteorder="big")
def bytes2uintle(input_bytes):
return int.from_bytes(input_bytes, byteorder="little")
def bytes2hex(input_bytes):
return input_bytes.hex()
def bytes2str(input_bytes):
return input_bytes.decode()
def parse(input_bytes, item_iter):
item_dict = {}
for item in item_iter:
name, a_slice, function = item
result = input_bytes[a_slice]
if function:
result = function(result)
item_dict[name] = result
return item_dict
def mask_flag_dict(flag_bytes, flag_name_mask_iter):
flag_int = int.from_bytes(flag_bytes, byteorder="big")
flag_dict = {}
for flag_name, mask in flag_name_mask_iter:
flag_dict[flag_name] = mask & flag_int
return flag_dict
def get_domain(domain_str):
if len(domain_str) == 4:
country_code = domain_str[0:2]
subdomain_code = int(domain_str[2])
status = int(domain_str[3]) + int(0x04) # 0x04 to activate
domain_int = 0
domain_int |= CHARTOITA2[country_code[0]] << 11
domain_int |= CHARTOITA2[country_code[1]] << 6
domain_int |= subdomain_code << 3
domain_int |= status
return domain_int.to_bytes(2, byteorder="big")
else:
raise ValueError(f"domain '{domain_str}' 4 characters expected")
def circular_increment(value, min_value, max_value):
value += 1
# value must be between 'min_value' and 'max_value' inclusive
if value > max_value:
value = min_value
return value
def get_message_type(message: bytes):
try:
message_type = message[0]
except IndexError as ie:
print(f"IndexError occurs in get_message_type: \n\n{ie}")
return None
if message_type in (0x0b, 0x1c):
return int.from_bytes(message[:2], byteorder="big")
return message_type
def get_timestamp_of_message(message: bytes):
message_type = get_message_type(message)
if message_type in MESSAGE_TYPE_TIMESTAMP_OFFSET:
index = MESSAGE_TYPE_TIMESTAMP_OFFSET[message_type]
ts = int.from_bytes(message[index:index + 4], byteorder="big")
return ts
return None
def set_timestamp_of_message(message: bytes, ts):
message_type = get_message_type(message)
if message_type in MESSAGE_TYPE_TIMESTAMP_OFFSET:
index = MESSAGE_TYPE_TIMESTAMP_OFFSET[message_type]
message = (
message[:index]
+ int(ts).to_bytes(4, byteorder="big")
+ message[index + 4:]
)
return message

399
static/index.css Normal file
View File

@@ -0,0 +1,399 @@
/* ** CSS styles ** */
/* LOGO_TEXT */
@import url('https://fonts.googleapis.com/css?family=Holtwood+One+SC');
.logo{
font-family: "Holtwood One SC", serif;
/*text-decoration: underline;*/
text-transform: capitalize;
text-align: center;
font-size: 80px;
color: rgb(75, 86, 87);
background-color: rgba(84, 84, 84, 0);
text-shadow: rgb(245, 230, 230) 2px 2px 2px;
}
/* NUMBER_SIZE */
#number {
width: 5em;
}
/* just_text */
.text {
font-family: "Times New Roman", Times, serif;
font-size: 20px;
color: #000;
}
/* INFO_HELP_text */
.info_number {
font-family: "Times New Roman", Times, serif;
font-size: 10px;
color: #000;
}
/* INFO_arguments_text */
.info_argument {
font-family: "Times New Roman", Times, serif;
font-size: 12px;
color: #000;
}
/* SWMU_text */
.swmu {
font-family: "Times New Roman", Times, serif;
font-size: 8px;
color: #000;
}
/* ign1a_info_text */
.ign1a_info {
font-family: "Times New Roman", Times, serif;/*font-family: "Audiowide", sans-serif;*/
font-size: 16px;
}
/* MODES_text */
.mode_text {
font-family: "Times New Roman", Times, serif;/*font-family: "Audiowide", sans-serif;*/
font-size: 18px;
color: #4E4545;
}
/* mode_text_alert */
.mode_text_alert {
font-family: "Times New Roman", Times, serif;/*font-family: "Audiowide", sans-serif;*/
font-size: 12px;
color: #D12A2A;
}
/* dropbox_text */
.dropbox_text {
font-family: "Times New Roman", Times, serif;/*font-family: "Audiowide", sans-serif;*/
font-size: 18px;
color: #4E4545;
/* vertical-align: top;*/
text-align: left;
}
/* Parameters_text */
.parameters_text {
font-family: "Times New Roman", Times, serif;/*font-family: "Audiowide", sans-serif;*/
font-size: 15px;
color: #4E4545;
}
/** IMAGE_swmu **/
.center {
display: block;
margin-left: auto;
margin-right: auto;
width: 50%;
}
/** DOWN_BUTT **/
.button_mode {
background-color: #ddd;
border: none;
color: black;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 4px 2px;
cursor: pointer;
border-radius: 16px;
}
/* Place the navbar at the bottom of the page, and make it stick */
.navbar {
background-color: #333;
overflow: hidden;
position: fixed;
bottom: 0;
width: 100%;
}
/* Style the links inside the navigation bar */
.navbar a {
float: left;
display: block;
color: #f2f2f2;
text-align: center;
padding: 14px 16px;
text-decoration: none;
font-size: 17px;
}
/* Change the color of links on hover */
.navbar a:hover {
background-color: #ddd;
color: black;
}
/* Add a color to the active/current link */
.navbar a.active {
background-color: #04AA6D;
color: white;
}
/* RADIO */
.radio_text {
font-family: "Times New Roman", Times, serif;
font-size: 20px;
color: #4E4545;
}
#content1, #content2, #content3 { display:none; }
#toggle1:checked ~ #content1
{
display: block;
}
#toggle2:checked ~ #content2
{
display: block;
}
#toggle3:checked ~ #content3
{
display: block;
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
/* RUN_BUTTON */
.run_butt {
align-items: center;
background-color: initial;
background-image: linear-gradient(#464d55, #25292e);
border-radius: 8px;
border-width: 0;
box-shadow: 0 10px 20px rgba(0, 0, 0, .1),0 3px 6px rgba(0, 0, 0, .05);
box-sizing: border-box;
color: #fff;
cursor: pointer;
display: inline-flex;
flex-direction: column;
/*font-family: expo-brand-demi,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";*/
/*font-family: "Audiowide", sans-serif;*/
font-family: "Times New Roman", Times, serif;
font-size: 24px;
height: 70px;
justify-content: center;
line-height: 1;
margin: 0;
outline: none;
overflow: hidden;
padding: 0 32px;
text-align: center;
text-decoration: none;
transform: translate3d(0, 0, 0);
transition: all 150ms;
vertical-align: baseline;
white-space: nowrap;
}
.run_butt:hover {
box-shadow: rgba(0, 1, 0, .2) 0 2px 8px;
opacity: .85;
}
.run_butt:active {
outline: 0;
}
.run_butt:focus {
box-shadow: rgba(0, 0, 0, .5) 0 0 0 3px;
}
@media (max-width: 420px) {
.run_butt {
height: 48px;
}
/** SELECTOR_INFO **/
body {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
height: 100vh;
background-color: #f1f1f1;
}
input {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
}
label {
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto;
}
select {
margin-bottom: 10px;
margin-top: 10px;
}
}
.selectt {
/*color: #fff;*/
padding: 30px;
display: none;
margin-top: 30px;
width: 60%;
/*background: grey*/
}
label {
margin-right: 20px;
}
/** BOX_ITEMS **/
.clearfix {
content: "";
clear: both;
display: table;
border-style: groove;
/*display: block;*/
/*margin-left: 700px;*/
margin-left: auto;
margin-right: auto;
width: 800px;
/*margin-right: auto;*/
}
/** HELP **/
helpswmu {
position: absolute;
right: 0;
}
/* CLEAR_BUTT */
.clear_butt {
align-items: center;
background-color: initial;
background-image: linear-gradient(#464d55, #8c2323);
border-radius: 8px;
border-width: 0;
box-shadow: 0 10px 20px rgba(0, 0, 0, .1),0 3px 6px rgba(0, 0, 0, .05);
box-sizing: border-box;
color: #fff;
cursor: pointer;
display: inline-flex;
flex-direction: column;
/*font-family: expo-brand-demi,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";*/
/*font-family: "Audiowide", sans-serif;*/
font-family: "Times New Roman", Times, serif;
font-size: 15px;
height: 20px;
justify-content: center;
line-height: 1;
margin: 0;
outline: none;
overflow: hidden;
padding: 0 10px;
text-align: center;
text-decoration: none;
transform: translate3d(0, 0, 0);
transition: all 150ms;
vertical-align: baseline;
white-space: nowrap;
}
.clear_butt:hover {
box-shadow: rgba(0, 1, 0, .2) 0 2px 8px;
opacity: .85;
}
.clear_butt:active {
outline: 0;
}
.clear_butt:focus {
box-shadow: rgba(0, 0, 0, .5) 0 0 0 3px;
}
@media (max-width: 420px) {
.clear_butt {
height: 48px;
}
/* file_butt */
.file_butt {
align-items: center;
background-color: initial;
background-image: linear-gradient(#464d55, #25292e);
border-radius: 8px;
border-width: 0;
box-shadow: 0 10px 20px rgba(0, 0, 0, .1),0 3px 6px rgba(0, 0, 0, .05);
box-sizing: border-box;
color: #fff;
cursor: pointer;
display: inline-flex;
flex-direction: column;
/*font-family: expo-brand-demi,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";*/
/*font-family: "Audiowide", sans-serif;*/
font-family: "Times New Roman", Times, serif;
font-size: 24px;
height: 70px;
justify-content: center;
line-height: 1;
margin: 0;
outline: none;
overflow: hidden;
padding: 0 32px;
text-align: center;
text-decoration: none;
transform: translate3d(0, 0, 0);
transition: all 150ms;
vertical-align: baseline;
white-space: nowrap;
}
.file_butt:hover {
box-shadow: rgba(0, 1, 0, .2) 0 2px 8px;
opacity: .85;
}
.file_butt:active {
outline: 0;
}
.file_butt:focus {
box-shadow: rgba(0, 0, 0, .5) 0 0 0 3px;
}
@media (max-width: 420px) {
.file_butt {
height: 48px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/pic/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

143
templates/helpswmu.html Normal file
View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<!-- Write your comments here -->
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='pic/favicon.ico') }}">
<label class="swmu">
<pre>environment: {{ environment }}</pre>
</label>
<div style="text-align: center;">
<label class="logo">SWMU</label>
</div>
<head>
<style>
body {
background-color: WhiteSmoke;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>
<!-- <script type=text/javascript> $(function() { $("#mybutton").click(function (event) { $.getJSON('/swmu_run', { },
function(data) { }); return false; }); }); </script> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Audiowide">
<title>SWMU</title>
</head>
<body>
<div>
<pre>
SWMU examples:
Example for 5 Software Mobile Units and server host `localhost`:
$ runswmu 5 --mode run
$ runswmu 5 --mode sim --log ./test_log.log
$ runswmu 5 --mode simfast --log ./test_log.log --simulate_pause 0.25
Other examples:
$ runswmu 1 --mode run --server-host localhost --start-id32 3567779841
$ runswmu 1 --mode sim --server-host localhost --start-id32 3567779841 --log ./test_log.log
$ runswmu 1 --mode run --server-host 10.48.172.135 --start-id32 3567779841 --bt-spontaneous-timeout 5 --simulate_pause 0.25
$ LOG_LEVEL="INFO" runswmu 5 --mode sim --server-host localhost --start-id32 3567779871 --bt-spontaneous-timeout 5 --simulate_pause 0.25 --log ./test_log.log
$ LOG_LEVEL="INFO" runswmu 2500 --mode run --server-host 10.48.172.135 --bt-spontaneous-timeout 20 --sigma 3 --sta True --sta-pause 60 --x01-pause 11 --x1d-pause 250 --x1a-pause 18 --x1a-type 1
--domain DE00 --near_by_domain CZ00 --start-id32 3567779841
$ runswmu 1 --mode run --server-host 10.48.172.135 --start-id32 3567779841 --bt-spontaneous-timeout 5 --simulate_pause 0.25
$ runswmu 50 --mode simfast --server-host localhost --start-id32 3567779841 --log ./test_log.log --simulate_pause 0.25
how to create pod (k8s):
$ kubectl [-n namespace] run [name_of_pod] --rm -i --tty --image [name_of_image] -- /bin/bash
$ kubectl -n uhura-azure-wagexp run swmu --namespace=uhura-azure-wagexp --image=principazureacr.azurecr.io/uhura/swmu-0.1.0 --requests=cpu=100m,memory=512Mi --limits=cpu=200m,memory=2048Mi --rm -i --tty -- /bin/bash
$ kubectl -n uhura-azure-wagexp run swmu --rm -i --tty --image=python -- /bin/bash
how to add mulog_file to k8s pod:
$ kubectl [-n namespace] cp [path to mulog file] [pod_name]:/
$ kubectl -n uhura-azure-wagexp cp /home/directory/test_log.log swmu:/data
SWMU arguments (command runswmu -h):
mandatory arguments:
UNITS unit amount
--mode MODE mode run | sim | simfast
optional arguments for "run" mode:
--latitude LATITUDE latitude (default 47.687622035085916)
--longitude LONGITUDE
longitude (default 17.744894027593666)
--domain DOMAIN current domain (default HU00)
--near_by_domain NEAR_BY_DOMAIN
near by domain (default HU00)
--x01 GENERATE_X01 generate x01 (default True)
--x01-pause X01_PAUSE
x01 pause in seconds (default 5s)
--units-wo-x1a UNITS_WO_X1A
amount of units that will not generate x1a (default 0)
--x1a-pause X1A_PAUSE
x1a pause in seconds (default 10s)
--x1a-mask X1A_MASK x1a mask-type of log: 01 - without Regain signal flag; 17 - with Regain signal flag (default)
--x1a-type X1A_TYPE x1a type of log (number of positions): 0 - (default); 1 - 17pos; 2 - 33pos
--x1d GENERATE_X1D generate x1d (default True)
--x1d-pause X1D_PAUSE
x1d pause in seconds (default 60sec)
--x1d-weight_axles X1D_WEIGHT_AXLES
x1d weight + number of axles: 'auto' - generate 2-10 (default); '42' - set weight 4, axle 2; '76' - set weight 7, axle 6;
optional arguments for "sim" or "simfast" mode:
--log IMPORT_DIR path to simulated log (obligatory with "sim" and "simfast" mode)
--simulate_pause SIMULATE_PAUSE
used for mode "simfast" to set delay between each message in seconds (default 2s)
--sim_time_check SIM_TIME_CHECK
used for mode 'sim' to check timestamp:
1) if 'no' is set, there is no changes in timestamp (default)
2) if 'max' is set, the the wait between two messages cannot be greater than 60s
3) if 'max_y' is set, the messages older then 5 years are ignored
--sim_stop SIM_STOP turn off/on auto-loop-close for sim/simfast mode (default True)
shared arguments:
-h, --help show this help message and exit
--server-host SERVER_HOST
server host (default localhost)
--server-port SERVER_PORT
server port (default 31001)
--start-id32 START_ID32
start id32 (default 3567779840)
--start-port START_PORT
start port (default 1024)
--bt-spontaneous-timeout BT_SPONTANEOUS_TIMEOUT
BT spontaneous timeout in seconds (default 20)
--sigma SIGMA sigma in seconds (default 0.2)
--sta SEND_STA send sta (default True)
--sta-pause STA_PAUSE
STA pause in seconds (default 30sec)
</pre>
</div>
<form action="/" method="get">
<div class="navbar">
<a href="/home" >Home</a>
<a href="/info" >info</a>
<a href="/helpswmu" class="active" >helpswmu</a>
<a href="/mulog_read" >mulog read</a>
<!--<a href="#examples">examples -->
<!--<a href="#about">about</a> -->
<a class="navbar-brand pull-sm-right m-r-0" style="float:right" href="https://gitlab.princip.cz/lab/sw/uhura/swmu" target="_blank">
<i class="fa fa-gitlab" aria-hidden="true"><span>&nbsp; &nbsp;</span>Gitlab</i>
</a>
<a href="https://muid.princip.cz/" style="float:right" target="_blank">muidconv</a>
<a href="https://xxxlog.cml.princip.cz/" style="float:right" target="_blank">xxlog</a>
</div>
</form>
</body>
</html>

24
templates/index.html Normal file
View File

@@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<title>The jQuery Example</title>
<h2>jQuery-AJAX in FLASK. Execute function on button click</h2>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>
<script type=text/javascript> $(function() { $("#mybutton").click(function (event) { $.getJSON('/SomeFunction', { },
function(data) { }); return false; }); }); </script>
<script type=text/javascript> $(function() { $("#mybutton2").click(function (event) { $.getJSON('/SomeFunction2', { },
function(data) { }); return false; }); }); </script>
<script type=text/javascript> $(function() { $("#mybutton3").click(function (event) { $.getJSON('/SomeFunction3', { },
function(data) { }); return false; }); }); </script>
</head>
<body>
<input type = "button" id = "mybutton" value = "Click Here" />
<input type = "button" id = "mybutton2" value = "Click Here" />
<input type = "button" id = "mybutton3" value = "Click Here" />
</body>
</html>

636
templates/index_v2.html Normal file
View File

@@ -0,0 +1,636 @@
<!DOCTYPE html>
<html lang="en">
<!-- Write your comments here -->
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='pic/favicon.ico') }}">
<label class="swmu">
<pre>environment: {{ environment }}</pre>
</label>
<div style="text-align: center;">
<!-- <label class="logo">SWMU</label> -->
<img src="static/pic/SWMU-logos_transparent3.png" alt="TEST" width="700" height="250">
</div>
<head>
<style>
body {
background-color: WhiteSmoke;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>
<script type=text/javascript> $(function() { $("#mybutton").click(function (event) { $.getJSON('/swmu_run', { },
function(data) { }); return false; }); }); </script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Audiowide">
<title>SWMU</title>
</head>
<body>
<form action="#">
</form>
<form action="/" method="POST" >
<!--<div class="text-center">-->
<div class="clearfix">
<div>
<label class="info_argument">Mandatory arguments:</label>
</div>
<label class="dropbox_text">log leves:
<select name="loglevels" id="loglevels">
<!--<option value="INFO" selected disabled hidden>Choose, default "INFO"</option>-->
<option value="INFO">INFO</option>
<option value="CRITICAL">CRITICAL</option>
<option value="ERROR">ERROR</option>
<option value="WARNING">WARNING</option>
<option value="DEBUG">DEBUG</option>
<option value="NOTSET">NOTSET</option>
</select>
<!--<input type="submit" value=" Set ">-->
<label class="info_number"> (default INFO)</label>
</label>
<div>
<label class="mode_text">start ID32:</label>
<input type="number" name="start-id32" value="3567779840"
min="3567779840" max="9999999999">
<label class="info_number"> (min 3567779840)</label>
</div>
<div>
<label class="mode_text">number of swmu: </label>
<input type="number" name="number_of_swmu" value="1"
min="1" max="99999">
<label class="info_number"> (min 1)</label>
</div>
<!-- RadioButtons + content -->
<div>
<label class="mode_text">modes:</label>
<label>
<input type="radio" name="modes"
value="run">run
</label>
<label>
<input type="radio" name="modes"
value="sim">sim
</label>
<label>
<input type="radio" name="modes"
value="simfast">simfast
</label>
<label class="info_number"> (default run)</label>
<label>
<input type="radio" name="modes" value="None" style="display:none" checked>
</label>
<!--
<label>
<input type="radio" name="modes"
value="XX"> XX</label>
-->
</div>
<div>&nbsp;</div>
<div>
<label class="info_argument">Shared arguments:</label>
</div>
<div>
<label class="mode_text">server host: </label>
<input type=text name="server_hosts" value="localhost">
<label class="info_number"> (default localhost)</label>
</div>
<div>
<label class="mode_text">server port: </label>
<input type="number" name="server_ports" value="31001">
<label class="info_number"> (default 31001)</label>
</div>
<div>
<label class="mode_text">start port: </label>
<input type="number" name="start_ports" value="1024">
<label class="info_number"> (default 1024)</label>
</div>
<div>
<label class="mode_text">bt spontaneous timeout: </label>
<input type="number" name="bt_spontaneous_timeouts" value="20">
<label class="info_number"> (default 20)</label>
</div>
<div>
<label class="mode_text">sigma: </label>
<input type="number" name="sigmas" value="0.2"
min="0" max="2" step="0.1">
<label class="info_number"> (default 0.2)</label>
</div>
<div>
<label class="mode_text">sta: </label>
<select name="stas">
<option value="True">True</option>
<option value="False">False</option>
</select>
<label class="info_number"> (default True)</label>
</div>
<div>
<label class="mode_text">sta pause: </label>
<input type="number" name="sta_pauses" value="30">
<label class="info_number"> (default 30s)</label>
</div>
<div>
<label class="mode_text">file-transfer delay: </label>
<input type="number" name="ft_delays" value="2"
min="0" max="30" step="0.1">
<label class="info_number"> (default 2s)</label>
</div>
<div>
<label class="mode_text">file-transfer mode: </label>
<select name="ft_modes">
<option value="keep_none">keep_none</option>
<option value="keep_all">keep_all</option>
</select>
<label class="info_number"> (default keep_none)</label>
</div>
<!-- run selectt -->
<div class="run selectt">
<label class="info_argument">Mode 'run' arguments:</label>
<div>
<label class="mode_text">latitude: </label>
<input type="number" name="latitudes" value="47.687622035085916"
step="0.000000000000001">
<label class="info_number"> (default 47.687622035085916)</label>
</div>
<div>
<label class="mode_text">longitude: </label>
<input type="number" name="longitudes" value="17.744894027593666"
step="0.000000000000001">
<label class="info_number"> (default 17.744894027593666)</label>
</div>
<div>
<label class="mode_text">domain: </label>
<select name="domains">
<option value="HU00">HU00</option>
<option value="AL00">AL00</option>
<option value="AQ00">AQ00</option>
<option value="AT00">AT00</option>
<option value="BA00">BA00</option>
<option value="BE00">BE00</option>
<option value="BG00">BG00</option>
<option value="BY00">BY00</option>
<option value="CH00">CH00</option>
<option value="CY00">CY00</option>
<option value="CZ00">CZ00</option>
<option value="DE00">DE00</option>
<option value="DK00">DK00</option>
<option value="EE00">EE00</option>
<option value="ES00">ES00</option>
<option value="FI00">FI00</option>
<option value="FR00">FR00</option>
<option value="GB00">GB00</option>
<option value="GI00">GI00</option>
<option value="GR00">GR00</option>
<option value="HR00">HR00</option>
<option value="IE00">IE00</option>
<option value="IT00">IT00</option>
<option value="LT00">LT00</option>
<option value="LU00">LU00</option>
<option value="LV00">LV00</option>
<option value="MC00">MC00</option>
<option value="MD00">MD00</option>
<option value="ME00">ME00</option>
<option value="MT00">MT00</option>
<option value="NL00">NL00</option>
<option value="NO00">NO00</option>
<option value="PL00">PL00</option>
<option value="PT00">PT00</option>
<option value="RO00">RO00</option>
<option value="RS00">RS00</option>
<option value="RU00">RU00</option>
<option value="SE00">SE00</option>
<option value="SI00">SI00</option>
<option value="SK00">SK00</option>
<option value="TR00">TR00</option>
<option value="UA00">UA00</option>
</select>
<label class="info_number"> (default HU00)</label>
</div>
<div>
<label class="mode_text">near by domain: </label>
<select name="near_by_domains">
<option value="HU00">HU00</option>
<option value="AL00">AL00</option>
<option value="AQ00">AQ00</option>
<option value="AT00">AT00</option>
<option value="BA00">BA00</option>
<option value="BE00">BE00</option>
<option value="BG00">BG00</option>
<option value="BY00">BY00</option>
<option value="CH00">CH00</option>
<option value="CY00">CY00</option>
<option value="CZ00">CZ00</option>
<option value="DE00">DE00</option>
<option value="DK00">DK00</option>
<option value="EE00">EE00</option>
<option value="ES00">ES00</option>
<option value="FI00">FI00</option>
<option value="FR00">FR00</option>
<option value="GB00">GB00</option>
<option value="GI00">GI00</option>
<option value="GR00">GR00</option>
<option value="HR00">HR00</option>
<option value="IE00">IE00</option>
<option value="IT00">IT00</option>
<option value="LT00">LT00</option>
<option value="LU00">LU00</option>
<option value="LV00">LV00</option>
<option value="MC00">MC00</option>
<option value="MD00">MD00</option>
<option value="ME00">ME00</option>
<option value="MT00">MT00</option>
<option value="NL00">NL00</option>
<option value="NO00">NO00</option>
<option value="PL00">PL00</option>
<option value="PT00">PT00</option>
<option value="RO00">RO00</option>
<option value="RS00">RS00</option>
<option value="RU00">RU00</option>
<option value="SE00">SE00</option>
<option value="SI00">SI00</option>
<option value="SK00">SK00</option>
<option value="TR00">TR00</option>
<option value="UA00">UA00</option>
</select>
<label class="info_number"> (default HU00)</label>
</div>
<div>
<label class="mode_text">x01: </label>
<select name="x01s">
<option value="True">True</option>
<option value="False">False</option>
</select>
<label class="info_number"> (default True)</label>
</div>
<div>
<label class="mode_text">x01 pause: </label>
<input type="number" name="x01_pauses" value="5">
<label class="info_number"> (default 5s)</label>
</div>
<div>
<label class="mode_text">units without x1a: </label>
<input type="number" name="units_wo_x1as" value="0">
<label class="info_number"> (default 0)</label>
</div>
<div>
<label class="mode_text">x1a pause: </label>
<input type="number" name="x1a_pauses" value="10">
<label class="info_number"> (default 10s)</label>
</div>
<div>
<label class="mode_text">x1a mask: </label>
<select name="x1a_masks">
<option value="17">17 - with Regain signal flag</option>
<option value="01">01 - without Regain signal flag</option>
</select>
<label class="info_number"> (default 17)</label>
</div>
<div>
<label class="mode_text">x1a type: </label>
<select name="x1a_types">
<option value="0">0</option>
<option value="1">1 - 17pos</option>
<option value="2">1 - 33pos</option>
</select>
<label class="info_number"> (default 17)</label>
</div>
<div>
<label class="mode_text">x1d: </label>
<select name="x1ds">
<option value="True">True</option>
<option value="False">False</option>
</select>
<label class="info_number"> (default True)</label>
</div>
<div>
<label class="mode_text">x1d pause: </label>
<input type="number" name="x1d_pauses" value="60">
<label class="info_number"> (default 60s)</label>
</div>
<div>
<label class="mode_text">x1d weight axle: </label>
<input type=text name="x1d_weight_axles" value="auto">
<div>
<label class="info_number"> (generate 2-10 (default) | 42 - set weight 4, axle 2 | 76 - set weight 7, axle 6)</label>
</div>
</div>
</div>
<!-- sim selectt -->
<div class="sim selectt">
<label class="info_argument">Mode 'sim' arguments:</label>
<div>
<label class="mode_text">path to log: </label>
<input type=text name="logs" value="/home/User/Documents/log.txt" size="40">
<div>
<label class="info_number"> path to simulated log (obligatory with "sim" and "simfast" mode)</label>
</div>
</div>
<div>
<label class="mode_text">sim time check: </label>
<select name="sim_time_checks">
<option value="no">'no' is set, there is no changes in timestamp</option>
<option value="max">'max' is set, the the wait between two messages cannot be greater than 60s</option>
<option value="max_y">'max_y' is set, the messages older then 5 years are ignored</option>
</select>
<label class="info_number"> (default 'no')</label>
</div>
<div>
<label class="mode_text">sim stop: </label>
<select name="sim_stops">
<option value="True">True</option>
<option value="False">False</option>
</select>
<label class="info_number"> (default True)</label>
</div>
<!-- simfast selectt -->
</div>
<div class="simfast selectt">
<label class="info_argument">Mode 'simfast' arguments:</label>
<div>
<label class="mode_text">path to log: </label>
<input type=text name="logs_simfast" value="/home/User/Documents/log.txt" size="40">
<div>
<label class="info_number"> path to simulated log (obligatory with "sim" and "simfast" mode)</label>
</div>
</div>
<div>
<label class="mode_text">sim time check: </label>
<select name="sim_time_checks_simfast">
<option value="no">'no' is set, there is no changes in timestamp</option>
<option value="max">'max' is set, the the wait between two messages cannot be greater than 60s</option>
<option value="max_y">'max_y' is set, the messages older then 5 years are ignored</option>
</select>
<label class="info_number"> (default 'no')</label>
</div>
<div>
<label class="mode_text">sim stop: </label>
<select name="sim_stops">
<option value="True">True</option>
<option value="False">False</option>
</select>
<label class="info_number"> (default True)</label>
</div>
<div>
<label class="mode_text">simulate pause: </label>
<input type="number" name="simulate_pauses" value="2">
<label class="info_number"> (default 2s)</label>
</div>
</div>
<!--
<div class="XX selectt">
<strong>XX</strong>
XX
</div>
-->
</div>
<!--</div>-->
<div class="text-center">
<input type="submit" name="submit_button" value="Generate" class="run_butt">
<input type="submit" name="submit_button" value="Clear" class="clear_butt">
</div>
<!-- <input type=submit name='submit'>-->
</form>
<script type="text/javascript">
$(document).ready(function() {
$('input[type="radio"]').click(function() {
var inputValue = $(this).attr("value");
var targetBox = $("." + inputValue);
$(".selectt").not(targetBox).hide();
$(targetBox).show();
});
});
</script>
<!--
<script type="text/javascript">
function myFunction() {
/* Get the text field */
var copyText = document.getElementById("myInput");
/* Select the text field */
copyText.select();
copyText.setSelectionRange(0, 99999); /* For mobile devices */
/* Copy the text inside the text field */
navigator.clipboard.writeText(copyText.value);
/* Alert the copied text */
alert("Copied the text: " + copyText.value);
}
</script>
<script type="text/javascript"> id="a" onclick="copyDivToClipboard()"
function copyDivToClipboard() {
var range = document.createRange();
range.selectNode(document.getElementById("a"));
window.getSelection().removeAllRanges(); // clear current selection
window.getSelection().addRange(range); // to select text
document.execCommand("copy");
window.getSelection().removeAllRanges();// to deselect
}
</script>
<form action="/" method="get">
<div class="text-center">
<button formaction="/swmu_run" class="run_butt" role="button">Generate</button>
</div>
</form>
-->
<!-- https://jinja.palletsprojects.com/en/3.0.x/ -->
<div style="text-align: auto;">
{% if clear == False %} <!-- clear -->
{% if questions %} <!-- to not see blank style -->
<div style="border: groove" >
{% for question in questions %}
{% if "None" == question.mode %} <!-- set default run mode -->
<div style="border: groove" >
LOG_LEVEL="{{question.loglevel}}" runswmu {{question.number_swmu}} --mode run --start-id32 {{question.id32}} --server-host {{question.server_host}} --server-port {{question.server_port}} --start-port {{question.start_port}} --bt-spontaneous-timeout {{question.bt_spontaneous_timeout}} --sigma {{question.sigma}} --sta {{question.sta}} --sta-pause {{question.sta_pause}} --ft_delay {{question.ft_delay}} --ft_mode {{question.ft_mode}}<br>
</div>
{% elif "run" == question.mode %}
<div style="border: groove" >
LOG_LEVEL="{{question.loglevel}}"
runswmu {{question.number_swmu}}
--mode {{question.mode}}
--start-id32 {{question.id32}}
--server-host {{question.server_host}}
--server-port {{question.server_port}}
--start-port {{question.start_port}}
--bt-spontaneous-timeout {{question.bt_spontaneous_timeout}}
--sigma {{question.sigma}}
--sta {{question.sta}}
--sta-pause {{question.sta_pause}}
--ft_delay {{question.ft_delay}}
--ft_mode {{question.ft_mode}}
--latitude {{question.latitude}}
--longitude {{question.longitude}}
--domain {{question.domain}}
--near_by_domain {{question.near_by_domain}}
--x01 {{question.x01}}
--x01-pause {{question.x01_pause}}
--units-wo-x1a {{question.units_wo_x1a}}
--x1a-pause {{question.x1a_pause}}
--x1a-mask {{question.x1a_mask}}
--x1a-type {{question.x1a_type}}
--x1d {{question.x1d}}
--x1d-pause {{question.x1d_pause}}
--x1d-weight_axles {{question.x1d_weight_axles}}<br>
</div>
{% elif "sim" == question.mode %}
<div style="border: groove" >
LOG_LEVEL="{{question.loglevel}}"
runswmu {{question.number_swmu}}
--mode {{question.mode}}
--start-id32 {{question.id32}}
--server-host {{question.server_host}}
--server-port {{question.server_port}}
--start-port {{question.start_port}}
--bt-spontaneous-timeout {{question.bt_spontaneous_timeout}}
--sigma {{question.sigma}}
--sta {{question.sta}}
--sta-pause {{question.sta_pause}}
--ft_delay {{question.ft_delay}}
--ft_mode {{question.ft_mode}}
--log {{question.log}}
--sim_time_check {{question.sim_time_check}}
--sim_stop {{question.sim_stop}}<br>
</div>
{% elif "simfast" == question.mode %}
<div style="border: groove" >
LOG_LEVEL="{{question.loglevel}}"
runswmu {{question.number_swmu}}
--mode {{question.mode}}
--start-id32 {{question.id32}}
--server-host {{question.server_host}}
--server-port {{question.server_port}}
--start-port {{question.start_port}}
--bt-spontaneous-timeout {{question.bt_spontaneous_timeout}}
--sigma {{question.sigma}}
--sta {{question.sta}}
--sta-pause {{question.sta_pause}}
--ft_delay {{question.ft_delay}}
--ft_mode {{question.ft_mode}}
--log {{question.log}}
--sim_time_check {{question.sim_time_check}}
--sim_stop {{question.sim_stop}}
--simulate_pause {{question.simulate_pause}}<br>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% else %}
<div></div>
{% endif %}
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
</div>
<form action="/" method="get">
<div class="navbar">
<a href="/home" class="active" >Home</a>
<a href="/info" >info</a>
<a href="/helpswmu" >helpswmu</a>
<a href="/mulog_read" >mulog read</a>
<!--<a href="#examples">examples -->
<!--<a href="#about">about</a> -->
<a class="navbar-brand pull-sm-right m-r-0" style="float:right" href="https://gitlab.princip.cz/lab/sw/uhura/swmu" target="_blank">
<i class="fa fa-gitlab" aria-hidden="true"><span>&nbsp; &nbsp;</span>Gitlab</i>
</a>
<a href="https://muid.princip.cz/" style="float:right" target="_blank">muidconv</a>
<a href="https://xxxlog.cml.princip.cz/" style="float:right" target="_blank">xxlog</a>
</div>
</form>
</body>
</html>

51
templates/info.html Normal file
View File

@@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<!-- Write your comments here -->
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='pic/favicon.ico') }}">
<label class="swmu">
<pre>environment: {{ environment }}</pre>
</label>
<div style="text-align: center;">
<label class="logo">SWMU</label>
</div>
<head>
<style>
body {
background-color: WhiteSmoke;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>
<!-- <script type=text/javascript> $(function() { $("#mybutton").click(function (event) { $.getJSON('/swmu_run', { },
function(data) { }); return false; }); }); </script> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Audiowide">
<title>SWMU</title>
</head>
<body>
<form action="/" method="get">
<div class="navbar">
<a href="/home" >Home</a>
<a href="/info" class="active" >info</a>
<a href="/helpswmu" >helpswmu</a>
<a href="/mulog_read" >mulog read</a>
<!--<a href="#examples">examples -->
<!--<a href="#about">about</a> -->
<a class="navbar-brand pull-sm-right m-r-0" style="float:right" href="https://gitlab.princip.cz/lab/sw/uhura/swmu" target="_blank">
<i class="fa fa-gitlab" aria-hidden="true"><span>&nbsp; &nbsp;</span>Gitlab</i>
</a>
<a href="https://muid.princip.cz/" style="float:right" target="_blank">muidconv</a>
<a href="https://xxxlog.cml.princip.cz/" style="float:right" target="_blank">xxlog</a>
</div>
</form>
</body>
</html>

202
templates/mulog_read.html Normal file
View File

@@ -0,0 +1,202 @@
<!DOCTYPE html>
<html lang="en">
<!-- Write your comments here -->
<link rel="stylesheet" href="{{ url_for('static', filename='index.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='pic/favicon.ico') }}">
<label class="swmu">
<pre>environment: {{ environment }}</pre>
</label>
<div style="text-align: center;">
<label class="logo">SWMU</label>
</div>
<head>
<style>
body {
background-color: WhiteSmoke;
}
table, th, td {
border:1px solid black;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"> </script>
<!--<script type=text/javascript> $(function() { $("#call_remove_butt").click(function (event) { $.getJSON('/call_remove', { },
function(data) { }); return false; }); }); </script>-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" crossorigin="anonymous">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Audiowide">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.1/min/dropzone.min.css">
<title>SWMU</title>
</head>
<body>
<div style="text-align: center;" >
<pre>supports only <b>.log</b> format with max file size <b>{{ max_size }}</b>mb</pre>
</div>
<div style="text-align: auto;">
<form action="{{ environment_road }}" method = "POST" enctype = "multipart/form-data">
<div class="clearfix">
<input type = "file" name = "file" class="text-left"/>
<!--<input type = "submit"/>-->
<button type="submit" class="file_butt"> load file </button>
<select id="ign1a" style="float:right" name="ign1a">
<option value="False">False</option>
<option value="True">True</option>
</select>
<label for="ign1a" class="ign1a_info" style="float:right"> ignore x1a positions</label>
</div>
<div class="clearfix">
{% if files %}
{% for file in files %}
<!--<br>name: {{file}}
<br>&nbsp;&nbsp;test: {{mulog}}-->
<div class="info_argument" style="text-align: left;" >
<a href="uploads/{{file}}" class="button">Download log</a>
<a href="uploads/{{file}}_pos_file.csv" class="button">Download position file</a>
<br>name: {{files}}
<table class="mode_text" style="width:100%">
<tr>
<td>x01_log_book_record: {{mulog[0]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x01" class="info_argument" target="_blank">info</a>
</td>
<td>x04_driver_login: {{mulog[1]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x04" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x05_driver_logout: {{mulog[2]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x05" class="info_argument" target="_blank">info</a>
</td>
<td>x08x09_log_record: {{mulog[3]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x08/01/10" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x08x09_log_record: {{mulog[4]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x08/190" class="info_argument" target="_blank">info</a>
</td>
<td>x13_driving_style: {{mulog[5]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x13" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x1400_accelerometer_event: {{mulog[6]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x1400" class="info_argument" target="_blank">info</a>
</td>
<td>x25_perfect_drive: {{mulog[7]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x25" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x26_long_mext: {{mulog[8]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x26" class="info_argument" target="_blank">info</a>
</td>
<td>x0b_log_record: {{mulog[9]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x0b81" class="info_argument" target="_blank">info</a>
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x0b85" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x0d_log_record: {{mulog[10]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x0d#0x0014" class="info_argument" target="_blank">info</a>
</td>
<td>x1a_fastlog: {{mulog[11]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x1a" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>x1c_log_record: {{mulog[12]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x1c" class="info_argument" target="_blank">info</a>
</td>
<td>x1d_domain_vehicle_parameters: {{mulog[13]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x1d" class="info_argument" target="_blank">info</a>
</td>
</tr>
<tr>
<td>0x0e_record: {{mulog[15]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x0e" class="info_argument" target="_blank">info</a>
</td>
<td>other_type: {{mulog[16]}}</td>
</tr>
<tr>
<td>0x10_record: {{mulog[14]}}
&nbsp;<a href="https://wiki.princip.cz/mediawiki/index.php/Doc-v2/log/0x10" class="info_argument" target="_blank">info</a>
</td>
</tr>
</table>
<table class="mode_text" style="width:100%">
<tr>
<td>number of timestamps: {{ts_c}}</td>
<td>number of positions: {{pos_c}}</td>
</tr>
</table>
{% if pos_c > max_pos %}
<table class="mode_text_alert" style="width:100%">
<tr align="center">
<td>recommended amount position for rendering is <b>{{max_pos}}</b>, otherwise, it may cause delay or instability </td>
</tr>
</table>
{% endif %}
{% if warn %}
<div class="info_argument" style="text-align: left;" >
timestamp in mulog is suspicious ([row in mulog], [message], [timestamp]): {list_ts_warning} {{warn}}
</div>
{% endif %}
{% if text %}
{{text}}
{% endif %}
</div>
{% endfor %}
{% endif %}
</div>
</form>
<div class="text-center">
<!--<input type = "button" id = "call_remove_butt" value = "Click Here" />
<input type="submit" name="submit_button" value="Clear" class="clear_butt">-->
{% if files %}
{% if text %}
{% else %}
<form action="/call_render" method="get" target="_blank">
<button name="subject" type="submit" class="run_butt" value="">Render map</button>
</form>
{% endif %}
{% endif %}
<form action="/call_remove" method="get">
<button name="subject" type="submit" class="clear_butt" value="">Clear</button>
</form>
</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
</div>
<form action="/" method="get">
<div class="navbar">
<a href="/home" >Home</a>
<a href="/info" >info</a>
<a href="/helpswmu" >helpswmu</a>
<a href="/mulog_read" class="active" >mulog read</a>
<!--<a href="#examples">examples -->
<!--<a href="#about">about</a> -->
<a class="navbar-brand pull-sm-right m-r-0" style="float:right" href="https://gitlab.princip.cz/lab/sw/uhura/swmu" target="_blank">
<i class="fa fa-gitlab" aria-hidden="true"><span>&nbsp; &nbsp;</span>Gitlab</i>
</a>
<a href="https://muid.princip.cz/" style="float:right" target="_blank">muidconv</a>
<a href="https://xxxlog.cml.princip.cz/" style="float:right" target="_blank">xxlog</a>
</div>
</form>
</body>
</html>

17
templates/test.html Normal file
View File

@@ -0,0 +1,17 @@
<!doctype html>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>File Upload</h1>
<form method="POST" action="" enctype="multipart/form-data">
<p><input type="file" name="file"></p>
<p><input type="submit" value="Submit"></p>
</form>
<hr>
{% for file in files %}
<img src="{{ url_for('upload', filename=file) }}" style="width: 64px">
{% endfor %}
</body>
</html>

0
uploads/.a Normal file
View File