#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Dec 11 13:40:11 2020 @author: Filip Biedziak """ import os import sys import websocket import threading import time import math import json from collections import OrderedDict from constants import HOST, PORT, NASAT_TELEGRAM_TYPES, LAT_BRE, LON_BRE, LAT_LTZ, LON_LTZ, R, Colors class NasatSocket(object): _websocket = None _host = HOST _port = PORT _status = False _statusThread = None _checkStatus = True _interrupt = True _verbose = 0 _telegram = OrderedDict() _buffer = [] _aircraftId = None _location = None _telegramType = None _timestamp = 0 _position = OrderedDict() _phi = 0.0 _dPhi = 6.0 * math.pi / 360.0 _lon = None _lat = None _fuseType = None _muncode = None _flightTime = None def __init__(self, telegramType, verbose): """Constructor.""" assert isinstance(telegramType, str) assert isinstance(verbose, int) if telegramType not in NASAT_TELEGRAM_TYPES: self.error("Unknown telegram type: %s" % telegramType) self._telegramType = telegramType self._telegram["type"] = self._telegramType self._telegram["data"] = OrderedDict() self._telegram["data"]["aircraftId"] = self._aircraftId self._verbose = verbose def error(self, msg): print("%s%sERROR%s: %s" % (Colors.BOLD, Colors.RED, Colors.RESET, msg)) self.close() sys.exit(0) def host(self): return self._host def setHost(self, host=None): assert isinstance(host, str) self._host = host def port(self): return self._port def setPort(self, port=None): assert isinstance(port, int) self._port = port def status(self): return self._status def checkStatus(self): return self._checkStatus def setCheckStatus(self, check): assert isinstance(check, bool) self._checkStatus = check def interrupt(self): return self._interrupt def setInterrupt(self, interrupt): assert isinstance(interrupt, bool) self._interrupt = interrupt def aircraftId(self): return self._aircraftId def setAircraftId(self, aircraftId): assert isinstance(aircraftId, int) if aircraftId < 0: self.error("Invalid aircraft ID: %d" % aircraftId) self._aircraftId = aircraftId self._telegram["data"]["aircraftId"] = self._aircraftId def telegramType(self): return self._telegramType def setPosition(self, lon, lat): self._lon = lon self._lat = lat def setMunition(self, muncode): self._muncode = muncode def setFuseType(self, fuseType): self._fuseType = fuseType def setFlightTime(self, flightTime): self._flightTime = flightTime def location(self): return self._location def setLocation(self, location): assert isinstance(location, str) if location not in ["Bremen", "Letzlingen"]: self.error("Unknown location: %s" % location) self._location = location # ==================================================== EXCON Connection ==================================================== def checkConnectionStatus(self): startTime = time.time() while self._checkStatus: changed = False # check if still connected if self._status: try: self._websocket.send("") # send an empty message except: self._status = False changed = True # attempt to reconnect else: if self._verbose > 1: print("Connecting to %s:%d" % (self._host, self._port)) try: self._websocket = websocket.create_connection("%s:%d" % (self._host, self._port)) self._status = True changed = True except: pass # only show when status changes if changed and self._verbose > 0: color = Colors.GREEN if self._status else Colors.RED status = "OK" if self._status else "not connected" print("Connection status: %s%s%s%s" % (Colors.BOLD, color, status, Colors.RESET)) # check every second time.sleep(1.0 - ((time.time() - startTime) % 1.0)) def connect(self): self._statusThread = threading.Thread(target=self.checkConnectionStatus) self._statusThread.start() def close(self): if self._verbose > 0: print("Closing connection.") self._checkStatus = False self._status = False if self._statusThread is not None: self._statusThread.do_run = False if self._websocket is not None: self._websocket.close() # ===================================================== NASAT Telegrams ==================================================== def start(self, **kwargs): if self._verbose > 1: print("Sending telegram of type: %s" % self._telegramType) if self._telegramType == "NasatPosition": if self._aircraftId is None: self.error("Aircraft ID has not been set.") elif self._location is None: self.error("Location has not been set.") self.simulateNasatPosition() elif self._telegramType == "NasatNineLiner": self.simulateNineLiner(**kwargs) elif self._telegramType == "NasatWeaponRelease": self.simulateWeaponRelease(**kwargs) else: self.error("Unknown telegram type: %s" % self._telegramType) def send(self): self._timestamp = time.time() self._telegram["data"]["timestamp"] = self._timestamp # print("send Telegram: %s" % self._telegram) telegram = json.dumps(self._telegram) if self._status: try: if self._verbose > 1: print("Sending telegram: %s" % telegram) self._websocket.send(telegram) except: self._buffer.append(telegram) # just in case of timing issues else: self._buffer.append(telegram) def sendBuffer(self): bufferCopy = [telegram for telegram in self._buffer] for telegram in bufferCopy: try: self._websocket.send(telegram) self._buffer = self._buffer[1:] time.sleep(0.1) except: break # ====================================================== NasatPosition ===================================================== def calculatePosition(self): lat = LAT_BRE if self._location == "Bremen" else LAT_LTZ if self._location == "Letzlingen" else 0 lon = LON_BRE if self._location == "Bremen" else LON_LTZ if self._location == "Letzlingen" else 0 self._position["lat"] = lat + R * math.cos(self._phi) self._position["lon"] = lon + R * math.sin(self._phi) * 1.7 # roughly a circle self._position["height"] = 1000 self._telegram["data"]["position"] = self._position self._phi += self._dPhi def simulateNasatPosition(self): startTime = time.time() count = 0 while True: if self._status and len(self._buffer) > 0: self.sendBuffer() self.calculatePosition() # update current telegram self.send() time.sleep(1.0 - ((time.time() - startTime) % 1.0)) if self._interrupt: if count == 10: self.close() elif count == 20: self.setCheckStatus(True) self.connect() count += 1 # ===================================================== NasatNineLiner ===================================================== def readNineLinerData(self, fname): assert isinstance(fname, str) if not os.path.exists(fname): self.error("NasatNineLiner telegrams requires a JSON file with additional data.") try: with open(fname, 'r') as f: data = json.load(f) except: self.error("Unable to read Nine Liner data from JSON file: %s" % fname) if data["data"]["timestamp"] == -1: data["data"]["timestamp"] = time.time() return data def simulateNineLiner(self, **kwargs): if "fname" not in kwargs.keys(): self.error("Missing argument 'fname' with path to Nine Liner data.") self._telegram = self.readNineLinerData(kwargs["fname"]) startTime = time.time() while not self._status: time.sleep(1.0 - ((time.time() - startTime) % 1.0)) # wait until connection has been established self.send() self.close() # ================================================= NasatWeaponRelease ================================================= def readWeaponReleaseData(self, fname): assert isinstance(fname, str) if not os.path.exists(fname): self.error("NasatWeaponRelease telegrams requires a JSON file with additional data. %s" % fname) try: with open(fname, 'r') as f: data = json.load(f) except: self.error("Unable to read NasatWeaponRelease data from JSON file: %s" % fname) if data["data"]["timestamp"] == -1: data["data"]["timestamp"] = time.time() return data def simulateWeaponRelease(self, **kwargs): if "fname" not in kwargs.keys(): self.error("Missing argument 'fname' with path to Weapon Release data.") self._telegram = self.readWeaponReleaseData(kwargs["fname"]) if (self._lon is not None and self._lat is not None): print("REPLACE pos") self._telegram["data"]["impactPosition"]["lat"] = self._lat self._telegram["data"]["impactPosition"]["lon"] = self._lon if (self._muncode is not None): print("Replace muncode") self._telegram["data"]["munition"] = self._muncode if (self._fuseType is not None): print("Replace fusetype") self._telegram["data"]["fuseType"] = self._fuseType if (self._flightTime is not None): print("Replace flightTime") self._telegram["data"]["flightTime"] = self._flightTime if (self._aircraftId is not None): print("Replace aircraft") self._telegram["data"]["aircraftId"] = self._aircraftId startTime = time.time() while not self._status: time.sleep(1.0 - ((time.time() - startTime) % 1.0)) # wait until connection has been established print("send NasatWeaponRelease with timestamp: %s" % self._telegram) self.send() self.close()