-
Notifications
You must be signed in to change notification settings - Fork 24
/
SendLog.py
executable file
·2407 lines (2015 loc) · 77.7 KB
/
SendLog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env python3
import gevent
from gevent.event import Event
from gevent.lock import RLock
from gevent.monkey import patch_all
from gevent.pywsgi import WSGIHandler, WSGIServer
patch_all(aggressive=False, subprocess=False)
from datetime import datetime
import fileinput
import flask
from flask import request, Response, session, render_template, jsonify
from flask_caching import Cache
import flask_session
from flaskext.versioned import Versioned
import glob
from importlib import reload
import json
import logging
from logging.handlers import TimedRotatingFileHandler
import sqlite3
import os
from os.path import abspath, dirname, exists, isfile, join, splitext
import re
import requests
import signal
import subprocess
from tempfile import mkdtemp, mkstemp
from threading import Timer, current_thread
from time import time
import uuid
from werkzeug.http import dump_cookie
from xml.dom import minidom
from werkzeug.utils import secure_filename
import config
class MyWSGIHandler(WSGIHandler):
def format_request(self):
length = self.response_length or '-'
if self.time_finish:
delta = '%.6f' % (self.time_finish - self.time_start)
else:
delta = '-'
if isinstance(self.client_address, tuple):
client_address = self.client_address[0]
else:
client_address = self.client_address
return '%s "%s" %s %s %s' % (
client_address or '-',
self.requestline or '',
(self._orig_status or self.status or '000').split()[0],
length,
delta)
class MyResponse(Response):
def set_cookie(self, *args, **kwargs):
cookie = dump_cookie(*args, **kwargs)
if kwargs.get('samesite', None) is None:
cookie = '%s; %s=%s' % (cookie,
'SameSite', config.SESSION_COOKIE_SAMESITE)
self.headers.add('Set-Cookie', cookie)
def makedirs(dirname, dirtype):
if not exists(dirname):
os.makedirs(dirname)
def rmdir(dirname, dirtype):
try:
if exists(dirname):
os.rmdir(dirname)
except Exception as e:
logger.warning('could not remove %s: %s', dirname, str(e))
def remove(filename):
if filename is None:
return False
if not config.REMOVEFILE:
logger.debug('not removing %s', filename)
return True
try:
os.remove(filename)
return True
except BaseException:
logger.error('could not remove %s', filename)
return False
# change directory before using relative paths
ROOTDIR = dirname(abspath(__file__))
os.chdir(ROOTDIR)
# Scilab dir
SCIDIR = abspath(config.SCILAB_DIR)
SCI = join(SCIDIR, "bin", "scilab-adv-cli")
READCONTENTFILE = abspath("resources/Read_Content.txt")
BASEDIR = abspath('webapp')
IMAGEDIR = join(BASEDIR, config.IMAGEDIR)
IMAGEURLDIR = '/' + config.IMAGEDIR + '/'
DB_NAME = abspath(config.DB_NAME)
XCOSSOURCEDIR = abspath(config.XCOSSOURCEDIR)
SESSIONDIR = abspath(config.SESSIONDIR)
FLASKSESSIONDIR = abspath(config.FLASKSESSIONDIR)
FLASKCACHINGDIR = abspath(config.FLASKCACHINGDIR)
LOGDIR = abspath(config.LOGDIR)
LOGFILE = join(LOGDIR, config.LOGFILE)
SYSTEM_COMMANDS = re.compile(config.SYSTEM_COMMANDS)
SPECIAL_CHARACTERS = re.compile(config.SPECIAL_CHARACTERS)
makedirs(FLASKSESSIONDIR, 'top flask session')
makedirs(SESSIONDIR, 'top session')
makedirs(FLASKCACHINGDIR, 'top flask caching')
makedirs(LOGDIR, 'log')
cache = Cache(config={
'CACHE_TYPE': 'filesystem',
'CACHE_DEFAULT_TIMEOUT': config.FLASKCACHINGDEFAULTTIMEOUT,
'CACHE_DIR': FLASKCACHINGDIR})
app = flask.Flask(__name__, static_folder='webapp/', template_folder='webapp')
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = FLASKSESSIONDIR
app.config['SESSION_COOKIE_SAMESITE'] = config.SESSION_COOKIE_SAMESITE
app.config['SESSION_COOKIE_SECURE'] = config.SESSION_COOKIE_SECURE
# These are the extension that we are accepting to be uploaded
app.config['ALLOWED_EXTENSIONS'] = set(['zcos', 'xcos', 'txt'])
app.response_class = MyResponse
cache.init_app(app)
flask_session.Session(app)
versioned = Versioned(app)
logger = logging.getLogger('xcos')
logger.setLevel(logging.DEBUG)
handler = TimedRotatingFileHandler(LOGFILE,
when='midnight',
backupCount=config.LOGBACKUPCOUNT)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(
fmt='%(asctime)s %(threadName)s %(levelname)s %(message)s',
datefmt='%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
# This is the path to the upload directory and values directory
UPLOAD_FOLDER = 'uploads' # to store xcos file
VALUES_FOLDER = 'values' # to store files related to tkscale block
# to store uploaded sci files for sci-func block
SCRIPT_FILES_FOLDER = 'script_files'
# to store workspace files for TOWS_c block
WORKSPACE_FILES_FOLDER = 'workspace_files'
# Delay time to look for new line (in s)
LOOK_DELAY = 0.1
# States of the line
# to indicate initialization of block in log file is encountered
INITIALIZATION = 0
# to indicate ending of log file data for that block is encountered
ENDING = 1
# to indicate data is proper and can be read
DATA = 2
# to indicate there is no line in log file further
NOLINE = -1
# display limit for long strings
DISPLAY_LIMIT = 10
# handle scilab startup
SCILAB_START = (
"funcprot(0);lines(0,120);"
"clearfun('messagebox');"
"function messagebox(msg,title,icon,buttons,modal),disp(msg),endfunction;"
"function xinfo(msg),disp(msg),endfunction;"
"funcprot(1);")
SCILAB_END = "mode(2);quit();"
USER_DATA = {}
VERSIONED_CHECK_TIME = 0
VERSIONED_LOCK = RLock()
VERSIONED_FILES_MTIME = {}
def version_check():
global VERSIONED_CHECK_TIME
modified = False
if time() > VERSIONED_CHECK_TIME:
with VERSIONED_LOCK:
if time() > VERSIONED_CHECK_TIME:
reload(config)
modified = is_versioned_file_modified()
VERSIONED_CHECK_TIME = time() + config.VERSIONED_CHECK_INTERVAL
return modified
def is_versioned_file_modified():
modified = False
for f in config.VERSIONED_FILES:
last_mtime = VERSIONED_FILES_MTIME.get(f, None)
mtime = os.stat(join(BASEDIR, f)).st_mtime
if last_mtime is None:
VERSIONED_FILES_MTIME[f] = mtime
elif mtime > last_mtime:
VERSIONED_FILES_MTIME[f] = mtime
logger.debug('%s modified', f)
modified = True
if modified:
app.jinja_env.cache.clear()
return modified
class ScilabInstance:
proc = None
log_name = None
base = None
starttime = None
endtime = None
def __init__(self):
(self.proc, self.log_name) = prestart_scilab()
def __str__(self):
return "{pid: %s, log_name: %s}" % (self.proc.pid, self.log_name)
INSTANCES_1 = []
INSTANCES_2 = []
evt = Event()
def no_free_scilab_instance():
l1 = len(INSTANCES_1)
return l1 == 0
def too_many_scilab_instances():
l1 = len(INSTANCES_1)
l2 = len(INSTANCES_2)
return l1 >= config.SCILAB_MIN_INSTANCES or \
l1 + l2 >= config.SCILAB_MAX_INSTANCES
def start_scilab_instances():
l1 = len(INSTANCES_1)
l2 = len(INSTANCES_2)
lssi = min(config.SCILAB_START_INSTANCES,
config.SCILAB_MAX_INSTANCES - l2) - l1
if lssi > 0:
logger.info('can start %s instances', lssi)
return lssi
def print_scilab_instances():
l1 = len(INSTANCES_1)
l2 = len(INSTANCES_2)
msg = ''
if l1 > 0:
msg += ', free=' + str(l1)
if l2 > 0:
msg += ', in use=' + str(l2)
logger.info('instance count: %s', msg[2:])
FIRST_INSTANCE = True
def prestart_scilab_instances():
global FIRST_INSTANCE
current_thread().name = 'PreStart'
attempt = 1
while True:
while too_many_scilab_instances():
evt.wait()
for i in range(start_scilab_instances()):
instance = ScilabInstance()
proc = instance.proc
if proc is None:
gevent.thread.interrupt_main()
return
if FIRST_INSTANCE:
gevent.sleep(1)
for i in range(2, 4):
if proc.poll() is not None:
break
gevent.sleep(i)
if proc.poll() is not None:
(out, err) = proc.communicate()
out = re.sub(r'^[ !\\-]*\n', r'', out, flags=re.MULTILINE)
if out:
logger.info('=== Output from scilab console ===\n%s',
out)
if err:
logger.info('=== Error from scilab console ===\n%s',
err)
# Check for errors in Scilab
if 'Cannot find scilab-bin' in out:
logger.critical('scilab has not been built. '
'Follow the installation instructions')
gevent.thread.interrupt_main()
return
returncode = proc.returncode
msg = 'attempts' if attempt != 1 else 'attempt'
if attempt >= 4:
logger.critical('aborting after %s %s: rc = %s',
attempt, msg, returncode)
gevent.thread.interrupt_main()
return
logger.error('retrying after %s %s: rc = %s',
attempt, msg, returncode)
gevent.sleep(config.SCILAB_INSTANCE_RETRY_INTERVAL * attempt)
attempt += 1
FIRST_INSTANCE = True
continue
INSTANCES_1.append(instance)
attempt = 1
FIRST_INSTANCE = False
print_scilab_instances()
if too_many_scilab_instances():
evt.clear()
def get_scilab_instance():
global FIRST_INSTANCE
try:
while True:
instance = INSTANCES_1.pop(0)
proc = instance.proc
if proc.poll() is not None:
logger.warning('scilab instance exited: return code is %s',
proc.returncode)
FIRST_INSTANCE = True
if not too_many_scilab_instances():
evt.set()
if no_free_scilab_instance():
gevent.sleep(4)
continue
INSTANCES_2.append(instance)
print_scilab_instances()
if not too_many_scilab_instances():
evt.set()
return instance
except IndexError:
logger.error('No free instance')
return None
def remove_scilab_instance(instance):
try:
INSTANCES_2.remove(instance)
print_scilab_instances()
if not too_many_scilab_instances():
evt.set()
except ValueError:
logger.error('could not find instance %s', instance)
def stop_scilab_instance(base, createlogfile=False):
stop_instance(base.instance, createlogfile)
base.instance = None
def stop_instance(instance, createlogfile=False, removeinstance=True):
if instance is None:
logger.warning('no instance')
return
if not kill_scilab_with(instance.proc, signal.SIGTERM):
kill_scilab_with(instance.proc, signal.SIGKILL)
if removeinstance:
remove_scilab_instance(instance)
if instance.log_name is None:
if createlogfile:
logger.warning('empty diagram')
else:
# remove(instance.log_name)
instance.log_name = None
instance.base = None
def stop_scilab_instances():
if len(INSTANCES_1) > 0:
logger.info('stopping %s idle instances', len(INSTANCES_1))
while len(INSTANCES_1) > 0:
instance = INSTANCES_1.pop()
stop_instance(instance, removeinstance=False)
if len(INSTANCES_2) > 0:
logger.info('stopping %s busy instances', len(INSTANCES_2))
while len(INSTANCES_2) > 0:
instance = INSTANCES_2.pop()
stop_instance(instance, removeinstance=False)
def reap_scilab_instances():
current_thread().name = 'Reaper'
while True:
gevent.sleep(100)
remove_instances = []
for instance in INSTANCES_2:
if instance.endtime < time():
remove_instances.append(instance)
count = len(remove_instances)
if count == 0:
continue
logger.info('removing %s stale instances', count)
for instance in remove_instances:
base = instance.base
if base is None:
logger.warning('cannot stop instance %s', instance)
stop_instance(instance)
elif isinstance(base, Diagram):
kill_scilab(base)
elif isinstance(base, Script):
kill_script(base)
elif isinstance(base, SciFile):
kill_scifile(base)
else:
logger.warning('cannot stop instance %s', instance)
stop_instance(instance)
class Diagram:
diagram_id = None
# session dir
sessiondir = None
# store uploaded filename
xcos_file_name = None
# type of uploaded file
workspace_counter = 0
save_variables = set()
# workspace from script
workspace_filename = None
# tk count
tk_count = 0
# store log name
instance = None
# is thread running?
tkbool = False
tk_starttime = None
# in memory values
tk_deltatimes = None
tk_values = None
tk_times = None
# List to store figure IDs from log_name
figure_list = None
file_image = ''
def __init__(self):
self.figure_list = []
def __str__(self):
return "{instance: %s, tkbool: %s, figure_list: %s}" % (
self.instance, self.tkbool, self.figure_list)
def clean(self):
if self.instance is not None:
kill_scilab(self)
self.instance = None
if self.xcos_file_name is not None:
remove(self.xcos_file_name)
self.xcos_file_name = None
if self.workspace_filename is not None:
remove(self.workspace_filename)
self.workspace_filename = None
if self.file_image != '':
remove(join(IMAGEDIR, self.file_image))
self.file_image = ''
class Script:
script_id = None
sessiondir = None
filename = None
status = 0
instance = None
workspace_filename = None
def __str__(self):
return (
"{script_id: %s, filename: %s, status: %d, instance: %s, "
"workspace_filename: %s}") % (
self.script_id, self.filename, self.status, self.instance,
self.workspace_filename)
def clean(self):
if self.instance is not None:
kill_script(self)
self.instance = None
if self.filename is not None:
remove(self.filename)
self.filename = None
if self.workspace_filename is not None:
remove(self.workspace_filename)
self.workspace_filename = None
class SciFile:
'''Variables used in sci-func block'''
instance = None
def clean(self):
if self.instance is not None:
kill_scifile(self)
self.instance = None
class DataFile:
sessiondir = None
data_filename = None
def clean(self):
if self.data_filename is not None:
remove(self.data_filename)
self.data_filename = None
class UserData:
sessiondir = None
diagrams = None
scripts = None
datafiles = None
scriptcount = None
scifile = None
diagramlock = None
timestamp = None
def __init__(self):
self.sessiondir = mkdtemp(
prefix=datetime.now().strftime('%Y%m%d.'), dir=SESSIONDIR)
self.diagrams = []
self.datafiles = []
self.scripts = {}
self.scriptcount = 0
self.scifile = SciFile()
self.diagramlock = RLock()
self.timestamp = time()
def getscriptcount(self):
with self.diagramlock:
rv = self.scriptcount
self.scriptcount += 1
return str(rv)
def clean(self):
for diagram in self.diagrams:
diagram.clean()
self.diagrams = None
for script in self.scripts:
self.scripts[script].clean()
self.scripts = None
for datafile in self.datafiles:
datafile.clean()
self.datafiles = None
self.scifile.clean()
self.scifile = None
self.diagramlock = None
# name of workspace file
workspace = join(self.sessiondir, WORKSPACE_FILES_FOLDER,
"workspace.dat")
if exists(workspace):
remove(workspace)
sessiondir = self.sessiondir
rmdir(join(sessiondir, WORKSPACE_FILES_FOLDER), 'workspace files')
rmdir(join(sessiondir, SCRIPT_FILES_FOLDER), 'script files')
rmdir(join(sessiondir, VALUES_FOLDER), 'values')
rmdir(join(sessiondir, UPLOAD_FOLDER), 'upload')
rmdir(sessiondir, 'session')
def set_session():
if 'uid' not in session:
session['uid'] = str(uuid.uuid4())
uid = session['uid']
if not hasattr(current_thread(), 's_name'):
current_thread().s_name = current_thread().name
current_thread().name = 'S-%s-%s' % (current_thread().s_name[12:], uid[:6])
return uid
def init_session():
uid = set_session()
if uid not in USER_DATA:
USER_DATA[uid] = UserData()
ud = USER_DATA[uid]
ud.timestamp = time()
sessiondir = ud.sessiondir
makedirs(sessiondir, 'session')
makedirs(join(sessiondir, UPLOAD_FOLDER), 'upload')
makedirs(join(sessiondir, VALUES_FOLDER), 'values')
makedirs(join(sessiondir, SCRIPT_FILES_FOLDER), 'script files')
makedirs(join(sessiondir, WORKSPACE_FILES_FOLDER), 'workspace files')
return (ud.diagrams, ud.scripts, ud.getscriptcount, ud.scifile,
ud.datafiles, sessiondir, ud.diagramlock)
def clean_sessions(final=False):
current_thread().name = 'Clean'
totalcount = 0
cleanuids = []
for uid, ud in USER_DATA.items():
totalcount += 1
if final or time() - ud.timestamp > config.SESSIONTIMEOUT:
cleanuids.append(uid)
logger.info('cleaning %s/%s sessions', len(cleanuids), totalcount)
for uid in cleanuids:
current_thread().name = 'Clean-%s' % uid[:6]
try:
logger.info('cleaning')
ud = USER_DATA.pop(uid)
ud.clean()
except Exception as e:
logger.warning('could not clean: %s', str(e))
def clean_sessions_thread():
current_thread().name = 'Clean'
while True:
gevent.sleep(config.SESSIONTIMEOUT / 2)
try:
clean_sessions()
except Exception as e:
logger.warning('Exception in clean_sessions: %s', str(e))
def get_diagram(xcos_file_id, remove=False):
if not xcos_file_id:
logger.warning('no id')
return None
xcos_file_id = int(xcos_file_id)
(diagrams, __, __, __, __, __, __) = init_session()
if xcos_file_id < 0 or xcos_file_id >= len(diagrams):
logger.warning('id %s not in diagrams', xcos_file_id)
return None
diagram = diagrams[xcos_file_id]
if remove:
diagrams[xcos_file_id] = Diagram()
return diagram
def add_diagram():
(diagrams, scripts, __, __, __, sessiondir, diagramlock) = init_session()
with diagramlock:
diagram = Diagram()
diagram.diagram_id = str(len(diagrams))
diagram.sessiondir = sessiondir
diagrams.append(diagram)
return (diagram, scripts, sessiondir)
def add_datafile():
(__, __, __, __, datafiles, sessiondir, __) = init_session()
datafile = DataFile()
datafile.sessiondir = sessiondir
datafiles.append(datafile)
return (datafile, sessiondir, str(len(datafiles)))
def get_script(script_id, scripts=None, remove=False):
if script_id is None:
return None
if not script_id:
logger.warning('no id')
return None
if scripts is None:
(__, scripts, __, __, __, __, __) = init_session()
if script_id not in scripts:
logger.warning('id %s not in scripts', script_id)
return None
script = scripts[script_id]
if remove:
del scripts[script_id]
return script
def add_script():
(__, scripts, getscriptcount, __, __, sessiondir, __) = init_session()
script_id = getscriptcount()
script = Script()
script.script_id = script_id
script.sessiondir = sessiondir
scripts[script_id] = script
return (script, sessiondir)
def parse_line(line, lineno):
'''
Function to parse the line
Returns tuple of figure ID and state
state = INITIALIZATION if new figure is created
ENDING if current fig end
DATA otherwise
'''
line_words = line.split(' ') # Each line is split to read condition
try:
# The below condition determines the block ID
if line_words[0] == "Initialization":
# New figure created
# Get fig id
# to extract figure ids (sometime multiple sinks can be used in one
# diagram to differentiate that)
figure_id = line_words[-1]
return (figure_id, INITIALIZATION)
elif line_words[0] == "Ending":
# Current figure end
# Get fig id
figure_id = line_words[-1]
return (figure_id, ENDING)
else:
# Current figure coordinates
figure_id = line_words[2]
return (figure_id, DATA)
except Exception as e:
logger.error('%s while parsing %s on line %s', str(e), line, lineno)
return (None, NOLINE)
def get_line_and_state(file, figure_list, lineno, incomplete_line):
'''
Function to get a new line from file
This also parses the line and appends new figures to figure List
'''
line = file.readline() # read line by line from log
if not line: # if line is empty then return noline
return (incomplete_line, NOLINE)
if incomplete_line is not None:
line = incomplete_line + line
if '\n' not in line:
return (line, NOLINE)
# every line is passed to function parse_line for getting values
line = line.rstrip()
parse_result = parse_line(line, lineno)
figure_id = parse_result[0]
state = parse_result[1]
if state == INITIALIZATION:
# New figure created
# Add figure ID to list
figure_list.append(figure_id) # figure id of block is added to list
return (None, INITIALIZATION)
elif state == ENDING:
# End of figure
# Remove figure ID from list
# Once ending of log file/data is encountered for that block, figure id
# will be removed
figure_list.remove(figure_id)
return (None, ENDING)
elif state == NOLINE:
return (None, NOLINE)
return (line, DATA)
logfilefdrlock = RLock()
LOGFILEFD = 123
def prestart_scilab():
cmd = SCILAB_START
cmdarray = [SCI,
"-nogui",
"-noatomsautoload",
"-nouserstartup",
"-nb",
"-nw",
"-e", cmd]
logfilefd, log_name = mkstemp(prefix=datetime.now().strftime(
'scilab-log-%Y%m%d-'), suffix='.txt', dir=SESSIONDIR)
with logfilefdrlock:
if logfilefd != LOGFILEFD:
os.dup2(logfilefd, LOGFILEFD)
os.close(logfilefd)
try:
proc = subprocess.Popen(
cmdarray,
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, start_new_session=True,
universal_newlines=True, pass_fds=(LOGFILEFD, ))
except FileNotFoundError:
logger.critical('scilab has not been built. '
'Follow the installation instructions')
proc = None
remove(log_name)
log_name = None
os.close(LOGFILEFD)
return (proc, log_name)
def run_scilab(command, base, createlogfile=False, timeout=70):
instance = get_scilab_instance()
if instance is None:
logger.error('cannot run command %s', command)
return None
cmd = command + SCILAB_END
logger.info('running command %s', cmd)
instance.proc.stdin.write(cmd)
if not createlogfile:
remove(instance.log_name)
instance.log_name = None
instance.base = base
instance.starttime = time()
instance.endtime = time() + timeout
return instance
def is_unsafe_script(filename):
'''
Read file and check for system commands and return error if file contains
system commands
'''
with open(filename, 'r') as f:
if not re.search(SYSTEM_COMMANDS, f.read()):
return False
# Delete saved file if system commands are encountered in that file
remove(filename)
return True
@app.route('/uploaddatafile', methods=['POST'])
def uploaddatafile():
'''
Below route is called for uploading audio/other file.
'''
# Get the au/other data file
file = request.files['file']
# Check if the data file is not null
if not file:
msg = "Error occured while uploading file. Please try again\n"
rv = {'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
(datafile, sessiondir, currlen) = add_datafile()
fname = join(sessiondir, UPLOAD_FOLDER, currlen + '@@'
+ secure_filename(file.filename))
file.save(fname)
datafile.data_filename = fname
rv = {'filepath': datafile.data_filename}
return Response(json.dumps(rv), mimetype='application/json')
@app.route('/uploadscript', methods=['POST'])
def uploadscript():
'''
Below route is called for uploading script file.
'''
(script, sessiondir) = add_script()
file = request.files['file']
if not file:
msg = "Upload Error\n"
rv = {'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
fname = join(sessiondir, SCRIPT_FILES_FOLDER,
script.script_id + '_script.sce')
file.save(fname)
script.filename = fname
if is_unsafe_script(fname):
msg = ("System calls are not allowed in script.\n"
"Please edit the script again.\n")
script.status = -1
rv = {'status': script.status, 'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
wfname = join(sessiondir, SCRIPT_FILES_FOLDER,
script.script_id + '_script_workspace.dat')
script.workspace_filename = wfname
command = "exec('%s');save('%s');" % (fname, wfname)
script.instance = run_scilab(command, script)
if script.instance is None:
msg = "Resource not available"
script.status = -2
rv = {'status': script.status, 'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
msg = ''
script.status = 1
rv = {'script_id': script.script_id, 'status': script.status, 'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
def clean_output(s):
'''handle whitespace and sequences in output'''
s = re.sub(r'[\a\b\f\r\v]', r'', s)
# https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences
s = re.sub(r'\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]', r'', s)
s = re.sub(r'\t', r' ', s)
s = re.sub(r' +(\n|$)', r'\n', s)
s = re.sub(r'\n+', r'\n', s)
s = re.sub(r'^\n', r'', s)
return s
@app.route('/getscriptoutput', methods=['POST'])
def getscriptoutput():
'''
Below route is called for uploading script file.
'''
script = get_script(get_script_id())
if script is None:
# when called with same script_id again or with incorrect script_id
logger.warning('no script')
msg = "no script"
rv = {'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
instance = script.instance
if instance is None:
logger.warning('no instance')
msg = "no instance"
rv = {'msg': msg}
return Response(json.dumps(rv), mimetype='application/json')
proc = instance.proc
try:
# output from scilab terminal is saved for checking error msg
output = proc.communicate(timeout=30)[0]
output = clean_output(output)
remove_scilab_instance(script.instance)
script.instance = None
returncode = proc.returncode
if returncode < 0 or returncode == 2:
logger.warning('return code is %s', returncode)
msg = 'Script stopped'
script.status = -5
rv = {'status': script.status, 'msg': msg, 'output': output}
return Response(json.dumps(rv), mimetype='application/json')