Skip to content

Commit

Permalink
Merge pull request #17 from texx00/feeder_sockets2class
Browse files Browse the repository at this point in the history
The feeder is now a library instead of being a different process.
In the updated version of the feeder class seems to be fixing #15 and #1.
  • Loading branch information
texx00 authored Oct 31, 2020
2 parents be4f2e8 + 51905ec commit b9a9261
Show file tree
Hide file tree
Showing 25 changed files with 484 additions and 433 deletions.
8 changes: 6 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@

FLASK_ENV=development
FLASK_DEBUG=0
SHOW_FEEDER_TERMINAL=true
RUN_FEEDER_MANUALLY=true

# feeder logger level: 5 -> acks received from the device (and above), level 6 -> lines sent to the device (and above), other standard logging levels of python
FEEDER_LEVEL=5

# flask logger level: uses standard python loggin levels (10-debug, 20-info, 30-warning, 40-error, 50-critical). Can set to warning to hide standard http requests
FLASK_LEVEL=30
24 changes: 0 additions & 24 deletions NCFeeder/run.py

This file was deleted.

83 changes: 0 additions & 83 deletions NCFeeder/socketio_interface.py

This file was deleted.

98 changes: 36 additions & 62 deletions UIserver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,44 @@
from flask_socketio import SocketIO
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate

import os
import sys
import logging
import platform

from subprocess import Popen
import psutil
import threading
import atexit
import signal
import urllib.request
import platform

from time import sleep
from UIserver.bot_interface.queue_manager import QueueManager
from dotenv import load_dotenv
import logging

import sass
from flask_minify import minify
from utils import settings_utils, software_updates

from UIserver.hw_controller.queue_manager import QueueManager
from UIserver.hw_controller.feeder import Feeder
from UIserver.hw_controller.feeder_event_manager import FeederEventManager
from UIserver.utils import settings_utils, software_updates



# Logging setup
load_dotenv()
level = os.getenv("FLASK_LEVEL")
if not level is None:
level = int(level)
else:
level = 0
settings_utils.print_level(level, "app")
logging.getLogger("werkzeug").setLevel(level)

# app setup
app = Flask(__name__, template_folder='templates')
app.logger.setLevel(logging.INFO)

app.config['SECRET_KEY'] = 'secret!' # TODO put a key here
app.config['UPLOAD_FOLDER'] = "./UIserver/static/Drawings"
Expand All @@ -31,16 +51,25 @@
db = SQLAlchemy(app)
migrate = Migrate(app, db)


# scss compiler (already minified)
sass.compile(dirname=(os.path.abspath(os.getcwd())+"/UIserver/static/scss", os.path.abspath(os.getcwd())+"/UIserver/static/css"), output_style='compressed')
# js and html minifier (on request)
minify(app=app, html=True, js=False)

app.qmanager = QueueManager(app, socketio)

import UIserver.database
import UIserver.views.drawings_management, UIserver.views.settings
import UIserver.bot_interface.socketio_callbacks
import UIserver.sockets_interface.socketio_callbacks
from UIserver.sockets_interface.socketio_emits import SocketioEmits

app.semits = SocketioEmits(app,socketio, db)

# Device controller initialization

app.feeder = Feeder(FeederEventManager(app))
app.feeder.connect()
app.qmanager = QueueManager(app, socketio)

# Context pre-processor variables
# Global template values to be injected before templates creation
Expand Down Expand Up @@ -70,61 +99,6 @@ def versioned_url_for(endpoint, **values):
values["version"] = sw_version
return url_for(endpoint, **values)

# This section starts the feeder or restarts it if already running when the server is restarted

# Wait until the server is ready
def wait_server_ready():
try:
while urllib.request.urlopen("http://localhost:5000").getcode() != 200:
pass
except Exception as e:
print("__init.py__ error: "+str(e))
start_feeder_process()

# run the waiting function in a thread
starter_thread = threading.Thread(target=wait_server_ready, daemon=True)
starter_thread.start()

# starts the process
def start_feeder_process():
try:
# If the "RUN_FEEDER_MANUALLY" environment variable is set to 'true', the server will not start the feeder which must then be started manually.
# Can be usefull when working on the feeder and it is not necessary to restart the server every time.
# To start the feeder manually can use "python NCFeeder/run.py" or also the debugger
if os.environ['RUN_FEEDER_MANUALLY'] == 'true':
return
except:
pass


# terminal window is available only on windows
if platform.system() == "Windows":
filename = os.path.dirname(__file__) + "\\..\\NCFeeder\\run.py"

from subprocess import CREATE_NEW_CONSOLE, CREATE_NO_WINDOW

try:
# Check if the environment variable is set. If it is will show the ncfeeder terminal window, otherwise will keep it hidden
create_window = CREATE_NEW_CONSOLE if os.environ['SHOW_FEEDER_TERMINAL'] == 'true' else CREATE_NO_WINDOW
except:
create_window = CREATE_NO_WINDOW
feeder_process = Popen("env/Scripts/activate.bat & python NCFeeder/run.py", env=os.environ.copy(), creationflags=create_window)
else:
filename = os.path.dirname(__file__) + "/../NCFeeder/run.py"
feeder_process = Popen(["python3", filename], env=os.environ.copy())
app.feeder_pid = feeder_process.pid

@atexit.register
def terminate_feeder_process():
try:
# The feeder_process cannot be killed or terminated if saved into the app directly.
# Instead of saving the process object save the pid and kill it with that
process = psutil.Process(app.feeder_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
except:
pass

# Home routes
@app.route('/')
Expand Down
82 changes: 82 additions & 0 deletions UIserver/hw_controller/device_serial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import serial.tools.list_ports
import serial
import time
import sys
import logging
import glob
from UIserver.hw_controller.emulator import Emulator

# This class connect to a serial device
# If the serial device request is not available it will create a virtual serial device


class DeviceSerial():
def __init__(self, serialname = None, baudrate = None, logger_name = None):
self.logger = logging.getLogger(logger_name) if not logger_name is None else logging.getLogger()
self.serialname = serialname
self.baudrate = baudrate
self.is_fake = False
self._buffer = bytearray()
self.echo = ""
self._emulator = Emulator()

try:
args = dict(
baudrate = self.baudrate,
timeout = 0,
write_timeout = 0
)
self.serial = serial.Serial(**args)
self.serial.port = self.serialname
self.serial.open()
self.logger.info("Serial device connected")
except:
#print(traceback.print_exc())
self.is_fake = True
self.logger.error("Serial not available. Will use the fake serial")

def send(self, obj):
if self.is_fake:
self._emulator.send(obj)
else:
if self.serial.is_open:
try:
while self.readline():
pass
self.serial.write(str(obj).encode())
except:
self.close()
self.logger.error("Error while sending a command")

def serial_port_list(self):
if sys.platform.startswith('win'):
plist = serial.tools.list_ports.comports()
ports = [port.device for port in plist]
elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
# this excludes your current terminal "/dev/tty"
ports = glob.glob('/dev/tty[A-Za-z]*')
else:
raise EnvironmentError('Unsupported platform')
return ports

def is_connected(self):
if(self.is_fake):
return False
return self.serial.is_open

def close(self):
try:
self.serial.close()
self.logger.info("Serial port closed")
except:
self.logger.error("Error: serial already closed or not available")

def readline(self):
if not self.is_fake:
if self.serial.is_open:
while self.serial.inWaiting():
line = self.serial.readline()
return line.decode(encoding='UTF-8')
else:
return self._emulator.readline()
return None
Loading

0 comments on commit b9a9261

Please sign in to comment.