mirror of
https://github.com/CopterExpress/clever-show.git
synced 2026-05-26 15:13:26 +00:00
Client with new config system
This commit is contained in:
124
Drone/client.py
124
Drone/client.py
@@ -1,18 +1,16 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import errno
|
||||
import random
|
||||
import socket
|
||||
import struct
|
||||
import logging
|
||||
import collections
|
||||
import ConfigParser
|
||||
import selectors2 as selectors
|
||||
import threading
|
||||
|
||||
from contextlib import closing
|
||||
|
||||
import os,sys,inspect # Add parent dir to PATH to import messaging_lib
|
||||
import inspect # Add parent dir to PATH to import messaging_lib
|
||||
current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||
parent_dir = os.path.dirname(current_dir)
|
||||
sys.path.insert(0, parent_dir)
|
||||
@@ -20,69 +18,40 @@ sys.path.insert(0, parent_dir)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
import messaging_lib as messaging
|
||||
import config
|
||||
from config import ConfigManager
|
||||
|
||||
ConfigOption = collections.namedtuple("ConfigOption", ["section", "option", "value"])
|
||||
active_client = None # needs to be refactored: Singleton \ factory callbacks
|
||||
|
||||
active_client = None # maybe needs to be refactored
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, config_path="client_config.ini"):
|
||||
def __init__(self, config_path="config/client.ini"):
|
||||
self.selector = selectors.DefaultSelector()
|
||||
self.client_socket = None
|
||||
|
||||
self.server_connection = messaging.ConnectionManager("pi")
|
||||
|
||||
self.server_host = None
|
||||
self.server_port = None
|
||||
self.broadcast_port = None
|
||||
|
||||
self.connected = False
|
||||
self.client_id = None
|
||||
|
||||
# Init configs
|
||||
self.config = ConfigManager()
|
||||
self.config_path = config_path
|
||||
self.config = ConfigParser.ConfigParser()
|
||||
self.load_config()
|
||||
|
||||
global active_client
|
||||
active_client = self
|
||||
|
||||
# self._last_ping_time = 0
|
||||
|
||||
def load_config(self):
|
||||
self.config.read(self.config_path)
|
||||
self.config.load_config_and_spec(self.config_path)
|
||||
|
||||
self.broadcast_port = self.config.getint('SERVER', 'broadcast_port')
|
||||
self.server_port = self.config.getint('SERVER', 'port')
|
||||
self.server_host = self.config.get('SERVER', 'host')
|
||||
self.BUFFER_SIZE = self.config.getint('SERVER', 'buffer_size')
|
||||
self.USE_NTP = self.config.getboolean('NTP', 'use_ntp')
|
||||
self.NTP_HOST = self.config.get('NTP', 'host')
|
||||
self.NTP_PORT = self.config.getint('NTP', 'port')
|
||||
|
||||
self.client_id = self.config.get('PRIVATE', 'id')
|
||||
if self.client_id == '/default':
|
||||
config_id = self.config.private_id.lower()
|
||||
if config_id == '/default':
|
||||
self.client_id = 'copter' + str(random.randrange(9999)).zfill(4)
|
||||
self.write_config(False, ConfigOption('PRIVATE', 'id', self.client_id))
|
||||
elif self.client_id == '/hostname':
|
||||
self.config.set('PRIVATE', 'id', self.client_id, write=True) # set and write
|
||||
elif config_id == '/hostname':
|
||||
self.client_id = socket.gethostname()
|
||||
elif self.client_id == '/ip':
|
||||
elif config_id == '/ip':
|
||||
self.client_id = messaging.get_ip_address()
|
||||
|
||||
def rewrite_config(self):
|
||||
with open(self.config_path, 'w') as file:
|
||||
self.config.write(file)
|
||||
os.system("chown -R pi:pi /home/pi/clever-show")
|
||||
|
||||
def write_config(self, reload_config=True, *config_options):
|
||||
for config_option in config_options:
|
||||
self.config.set(config_option.section, config_option.option, config_option.value)
|
||||
self.rewrite_config()
|
||||
|
||||
if reload_config:
|
||||
self.load_config()
|
||||
|
||||
@staticmethod
|
||||
def get_ntp_time(ntp_host, ntp_port):
|
||||
NTP_PACKET_FORMAT = "!12I"
|
||||
@@ -96,13 +65,15 @@ class Client(object):
|
||||
return unpacked[10] + float(unpacked[11]) / 2 ** 32 - NTP_DELTA
|
||||
|
||||
def time_now(self):
|
||||
if self.USE_NTP:
|
||||
timenow = self.get_ntp_time(self.NTP_HOST, self.NTP_PORT)
|
||||
if self.config.ntp_use:
|
||||
timenow = self.get_ntp_time(self.config.ntp_host, self.config.ntp_port)
|
||||
else:
|
||||
timenow = time.time()
|
||||
return timenow
|
||||
|
||||
def start(self):
|
||||
self.load_config()
|
||||
|
||||
logger.info("Starting client")
|
||||
messaging.NotifierSock().init(self.selector)
|
||||
|
||||
@@ -115,8 +86,8 @@ class Client(object):
|
||||
logger.critical("Caught interrupt, exiting!")
|
||||
self.selector.close()
|
||||
|
||||
def _reconnect(self, timeout=2.0, attempt_limit=3):
|
||||
logger.info("Trying to connect to {}:{} ...".format(self.server_host, self.server_port))
|
||||
def _reconnect(self, timeout=2.0, attempt_limit=3): # TODO reconnecting broadcast listener in another thread
|
||||
logger.info("Trying to connect to {}:{} ...".format(self.config.server_host, self.config.server_port))
|
||||
attempt_count = 0
|
||||
while not self.connected:
|
||||
logger.info("Waiting for connection, attempt {}".format(attempt_count))
|
||||
@@ -125,7 +96,7 @@ class Client(object):
|
||||
self.client_socket.settimeout(timeout)
|
||||
self.client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
self.client_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
self.client_socket.connect((self.server_host, self.server_port))
|
||||
self.client_socket.connect((self.config.server_host, self.config.server_port))
|
||||
except socket.error as error:
|
||||
if isinstance(error, OSError):
|
||||
if error.errno == errno.EINTR:
|
||||
@@ -149,21 +120,25 @@ class Client(object):
|
||||
def _connect(self):
|
||||
self.connected = True
|
||||
self.client_socket.setblocking(False)
|
||||
events = selectors.EVENT_READ # | selectors.EVENT_WRITE
|
||||
self.selector.register(self.client_socket, events, data=self.server_connection)
|
||||
self.server_connection.connect(self.selector, self.client_socket, (self.server_host, self.server_port))
|
||||
self.selector.register(self.client_socket, selectors.EVENT_READ, data=self.server_connection)
|
||||
self.server_connection.connect(self.selector, self.client_socket,
|
||||
(self.config.server_host, self.config.server_port))
|
||||
|
||||
def broadcast_bind(self, timeout=2.0, attempt_limit=3):
|
||||
broadcast_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
broadcast_client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||
broadcast_client.bind(("", self.broadcast_port))
|
||||
broadcast_client.settimeout(timeout)
|
||||
try:
|
||||
broadcast_client.bind(("", self.config.broadcast_port))
|
||||
except socket.error as error:
|
||||
logger.error("Error during broadcast listening binding: {}".format(error))
|
||||
return
|
||||
|
||||
attempt_count = 0
|
||||
try:
|
||||
while attempt_count <= attempt_limit:
|
||||
try:
|
||||
data, addr = broadcast_client.recvfrom(self.BUFFER_SIZE)
|
||||
data, addr = broadcast_client.recvfrom(self.config.server_buffer_size)
|
||||
except socket.error as error:
|
||||
logger.warning("Could not receive broadcast due error: {}".format(error))
|
||||
attempt_count += 1
|
||||
@@ -175,33 +150,27 @@ class Client(object):
|
||||
logger.info("Received broadcast message {} from {}".format(message.content, addr))
|
||||
if message.content["command"] == "server_ip":
|
||||
args = message.content["args"]
|
||||
self.server_port = int(args["port"])
|
||||
self.server_host = args["host"]
|
||||
self.write_config(False,
|
||||
ConfigOption("SERVER", "port", self.server_port),
|
||||
ConfigOption("SERVER", "host", self.server_host))
|
||||
logger.info("Binding to new IP: {}:{}".format(self.server_host, self.server_port))
|
||||
self.config.set("SERVER", "port", int(args["port"]))
|
||||
self.config.set("SERVER", "host", args["host"])
|
||||
self.config.write()
|
||||
|
||||
logger.info("Binding to new IP: {}:{}".format(
|
||||
self.config.server_host, self.config.server_port))
|
||||
self.on_broadcast_bind()
|
||||
break
|
||||
finally:
|
||||
broadcast_client.close()
|
||||
|
||||
def on_broadcast_bind(self):
|
||||
def on_broadcast_bind(self): # TODO move ALL binding code here
|
||||
pass
|
||||
|
||||
def _process_connections(self):
|
||||
while True:
|
||||
events = self.selector.select(timeout=1)
|
||||
# if time.time() - self._last_ping_time > 5:
|
||||
# self.server_connection.send_message("ping")
|
||||
# self._last_ping_time = time.time()
|
||||
# logging.debug("tick")
|
||||
|
||||
for key, mask in events:
|
||||
connection = key.data
|
||||
if connection is None:
|
||||
pass
|
||||
else:
|
||||
if connection is not None:
|
||||
try:
|
||||
connection.process_events(mask)
|
||||
|
||||
@@ -228,19 +197,28 @@ class Client(object):
|
||||
return
|
||||
|
||||
|
||||
@messaging.message_callback("config_write")
|
||||
@messaging.message_callback("config")
|
||||
def _command_config_write(*args, **kwargs):
|
||||
options = [ConfigOption(**raw_option) for raw_option in kwargs["options"]]
|
||||
logger.info("Writing config_attrs options: {}".format(options))
|
||||
active_client.write_config(kwargs["reload"], *options)
|
||||
print(kwargs)
|
||||
mode = kwargs.get("mode", "modify")
|
||||
# exceptions would be risen in case of incorrect config
|
||||
if mode == "rewrite":
|
||||
active_client.config.load_from_dict(kwargs["config"], path=active_client.config_path) # with validation
|
||||
elif mode == "modify":
|
||||
new_config = ConfigManager()
|
||||
new_config.load_from_dict(kwargs["config"])
|
||||
active_client.config.merge(new_config, validate=True)
|
||||
|
||||
active_client.config.write()
|
||||
active_client.load_config()
|
||||
|
||||
|
||||
@messaging.request_callback("id")
|
||||
def _response_id(*args, **kwargs):
|
||||
new_id = kwargs.get("new_id", None)
|
||||
if new_id is not None:
|
||||
cfg = ConfigOption("PRIVATE", "id", new_id)
|
||||
active_client.write_config(True, cfg)
|
||||
active_client.config.set("PRIVATE", "id", new_id, True)
|
||||
active_client.load_config()
|
||||
|
||||
return active_client.client_id
|
||||
|
||||
|
||||
@@ -431,7 +431,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
if not ok or not mode:
|
||||
return
|
||||
|
||||
path = QFileDialog.getOpenFileName(self, "Select configuration file", filter="Configs (*.ini *.txt .cfg)")[0]
|
||||
path = QFileDialog.getOpenFileName(self, "Select configuration file", filter="Configs (*.ini *.txt *.cfg)")[0]
|
||||
if not path:
|
||||
return
|
||||
|
||||
|
||||
Reference in New Issue
Block a user