Skip to content

Commit

Permalink
Merge pull request #589 from FreeTAKTeam/581-TCPtoDP
Browse files Browse the repository at this point in the history
converted tcp and ssl services to proper digitalpy implementations
  • Loading branch information
naman108 committed May 14, 2023
2 parents 3109d05 + 418f212 commit 9fff5a3
Show file tree
Hide file tree
Showing 40 changed files with 1,897 additions and 1,347 deletions.
15 changes: 15 additions & 0 deletions FreeTAKServer/components/core/abstract_component/cot_node.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import copy
from digitalpy.core.domain.node import Node
from FreeTAKServer.model.FTSModel.fts_protocol_object import FTSProtocolObject
import inspect
from lxml import etree


class CoTNode(Node, FTSProtocolObject):
def __init__(self, node_type, configuration, model, oid=None):
# a dictionary containing the attributes of all cots
self.cot_attributes = {}
# an xml object containing the contents of a given CoT without corresponding domain definition
self.xml = etree.Element(self.__class__.__name__)
self.text = ""
super().__init__(node_type, configuration, model, oid)

Expand All @@ -25,3 +30,13 @@ def add_child(self, child):
child.set_parent(self)
else:
raise TypeError("child must be an instance of Node")

def __getstate__(self):
state = self.__dict__
state_cp = copy.copy(state)
state_cp["xml"] = etree.tostring(self.xml)
return state_cp

def __setstate__(self, state):
state["xml"] = etree.fromstring(state["xml"])
self.__dict__ = state
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
from digitalpy.core.main.controller import Controller
from digitalpy.core.telemetry.tracer import Tracer
from digitalpy.core.zmanager.request import Request
from digitalpy.core.zmanager.response import Response
from digitalpy.core.zmanager.action_mapper import ActionMapper
from digitalpy.core.digipy_configuration.configuration import Configuration
from digitalpy.core.domain.node import Node

from lxml import etree

class DictToNodeController(Controller):
def __init__(
self,
request: Request,
response: Response,
sync_action_mapper: ActionMapper,
configuration: Configuration,
) -> None:
super().__init__(request, response, sync_action_mapper, configuration)

def execute(self, method=None):
return getattr(self, method)(**self.request.get_values())

Expand All @@ -23,17 +36,14 @@ def convert_dict_to_node(
tracer (Tracer, optional): a tracer object used for logging which should be passed by the facade. Defaults to None.
"""
with tracer.start_as_current_span("convert_dict_to_node") as span:
try:
span.add_event(
"serializing " + str(dictionary) + " to node " + str(model_object)
)
self.response.set_value(
"model_object",
self.serialize(dictionary[object_class_name.lower()], model_object),
)
span.add_event("serialization completed successfully")
except Exception as ex:
span.record_exception(ex)
span.add_event(
"serializing " + str(dictionary) + " to node " + str(model_object)
)
self.response.set_value(
"model_object",
self.serialize(dictionary[object_class_name.lower()], model_object),
)
span.add_event("serialization completed successfully")

def serialize(self, dictionary, node):
"""recursively serialize a single layer of the given dictionary
Expand All @@ -44,12 +54,49 @@ def serialize(self, dictionary, node):

def add_value_to_node(self, key, value, node):
"""add a value to a node object"""
if key == "#text":
setattr(node, "INTAG", value)

if isinstance(value, dict):
elif not hasattr(node, key.strip("@")):
self.handle_missing_attribute(key, value, node)

# this and ensures that the value is absoloutly a Node type instead of an expected list with only value, ex. a pm to one user would be a single object in the dict representation
# however it's expected to be a list
elif isinstance(value, dict) and isinstance(getattr(node, key, None), Node):
self.serialize(value, getattr(node, key))

elif isinstance(value, list):
elif isinstance(value, list) and isinstance(getattr(node, key, None), list):
for l_item in value:
self.add_value_to_node(key, l_item, node)

elif isinstance(getattr(node, key, None), list):
self.serialize(value, getattr(node, key)[0])

else:
setattr(node, key.strip("@"), value)

def handle_missing_attribute(self, key, value, node):
"""given the dynamic and expanding domain of the CoT standard there is always a risk of undocumented or new attributes or tags
appearing in a given cot which does not yet exist in the domain, in these cases the keys and values will be converted to xml strings
and added to the cot_node
Args:
key (str): attribute or tag
value (Union[str, dict, List]): object or objects of the attribute/tag
node (str): model object to store xml_string
"""
# this if case handles undefined nested objects
if isinstance(value, dict):
self.request.set_value("xml_dict", {key: value})
response = self.execute_sub_action("DictToXML")
elem = etree.fromstring(response.get_value("xml").encode())
node.xml.append(elem)
elif isinstance(value, list):
for l_item in value:
self.add_value_to_node(key, l_item, node)
response = self.execute_sub_action("DictToXML")
elem = etree.Element(key, response.get_value("message"))
node.xml.append(elem)

elif value is not None:
node.xml.attrib[key] = value
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@
from .xml_serializer import XmlSerializer
from .xml_element import ExtendedElement
from FreeTAKServer.model.FTSModel.fts_protocol_object import FTSProtocolObject

from ...domain import Domain

class XMLSerializationOrchestrator(Controller):
def __init__(
self,
request: Request,
response: Response,
action_mapper: ActionMapper,
sync_action_mapper: ActionMapper,
configuration: Configuration,
):
super().__init__(
request=request,
response=response,
action_mapper=action_mapper,
action_mapper=sync_action_mapper,
configuration=configuration,
)
self.domain_controller = Domain(request, response, sync_action_mapper, configuration)

def initialize(self, request, response):
super().initialize(request, response)
self.domain_controller.initialize(request, response)

def execute(self, method=None):
getattr(self, method)(**self.request.get_values())
Expand All @@ -49,11 +54,7 @@ def xml_to_node(self, message, model_object, **kwargs):
self.response.set_value("model_object", model_object)

def _get_parent(self, node, type=""):
self.request.set_value("node", node)
self.request.set_context(type)
sub_response = self.execute_sub_action("GetNodeParent")
return sub_response.get_value("parent")

return self.domain_controller.get_parent(node)

class TargetXMLToModel_object:
def __init__(self, model_object, parent_getter):
Expand Down
4 changes: 4 additions & 0 deletions FreeTAKServer/components/core/domain/domain/_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import datetime
from FreeTAKServer.components.core.abstract_component.cot_node import CoTNode
from FreeTAKServer.components.core.abstract_component.cot_property import CoTProperty
from lxml import etree

DATETIME_FMT = "%Y-%m-%dT%H:%M:%S.%fZ"
DEFAULT_STALE_TIME = 60
Expand All @@ -27,6 +28,9 @@ class Event(CoTNode):
def __init__(self, configuration: Configuration, model, oid=None):

super().__init__(self.__class__.__name__, configuration, model, oid)
# modify the xml object to be event instead of Event
self.xml = etree.Element(self.__class__.__name__.lower())

self.cot_attributes["version"] = None
self.cot_attributes["uid"] = None
self.cot_attributes["type"] = None
Expand Down
4 changes: 2 additions & 2 deletions FreeTAKServer/components/core/domain/domain/_marti.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def __init__(self, configuration: Configuration, model):

@CoTProperty
def dest(self):
return self.cot_attributes["dest"]
return self.get_children_ex(children_type="dest")

@dest.setter
def dest(self, dest):
self.cot_attributes["dest"].append(dest)
self.add_child(dest)
39 changes: 37 additions & 2 deletions FreeTAKServer/components/core/domain/domain_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
INTERNAL_ACTION_MAPPING_PATH,
MANIFEST_PATH,
)

from .controllers.dict_to_node_controller import DictToNodeController
from .controllers.domain import Domain as DomainController
from . import base

config = MainConfig.instance()
Expand All @@ -18,7 +21,7 @@ class Domain(DefaultFacade):

def __init__(
self,
domain_action_mapper,
sync_action_mapper,
request,
response,
configuration,
Expand All @@ -34,7 +37,7 @@ def __init__(
# the package containing the base classes
base=base,
# the component specific action mapper (passed by constructor)
action_mapper=domain_action_mapper,
action_mapper=sync_action_mapper,
# the request object (passed by constructor)
request=request,
# the response object (passed by constructor)
Expand All @@ -48,3 +51,35 @@ def __init__(
# the path to the manifest file
manifest_path=MANIFEST_PATH,
)
self.dict_to_node_controller = DictToNodeController(request, response, sync_action_mapper, configuration)
self.domain_controller = DomainController(request, response, sync_action_mapper, configuration)

def initialize(self, request, response):
super().initialize(request, response)
self.dict_to_node_controller.initialize(request, response)
self.domain_controller.initialize(request, response)

def execute(self, method):
try:
if hasattr(self, method):
getattr(self, method)(**self.request.get_values())
else:
self.request.set_value("logger", self.logger)
self.request.set_value("config_loader", self.config_loader)
self.request.set_value("tracer", self.tracer)
response = self.execute_sub_action(self.request.get_action())
self.response.set_values(response.get_values())
except Exception as e:
self.logger.fatal(str(e))

@DefaultFacade.public
def convert_dict_to_node(self, *args, **kwargs):
self.dict_to_node_controller.convert_dict_to_node(*args,**kwargs)

@DefaultFacade.public
def create_node(self, *args, **kwargs):
self.domain_controller.create_node(*args, **kwargs)

@DefaultFacade.public
def get_node_parent(self, *args, **kwargs):
self.domain_controller.get_parent(*args, **kwargs)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
; dict (Dict): dictionary representation of the xml string message
??XMLToDict = FreeTAKServer.components.core.xml_serializer.xml_serializer_facade.XmlSerializer.XMLToDict

; converts the provided dictionary to an xml string
; Args:
; xml_dict (Dict): dictionary representation of the xml string message
;
; Returns:
; message (str): dictionary in form of xml string
??DictToXML = FreeTAKServer.components.core.xml_serializer.xml_serializer_facade.XmlSerializer.DictToXML

;this action converts the provided node to an xml string
;Args:
; node (Node): the node to be serialized to xml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ ComponentName??Type = absolute.path.to.xml_serializer.controllers.xml_serializer

??XMLToDict = FreeTAKServer.components.core.xml_serializer.controllers.xml_serialization_controller.XMLSerializationController.convert_xml_to_dict

??DictToXML = FreeTAKServer.components.core.xml_serializer.controllers.xml_serialization_controller.XMLSerializationController.convert_dict_to_xml

??BroadcastComponentName = absolute.path.to.xml_serializer.controllers.xml_serializer_sender_controller.ComponentNameSenderController.broadcast_xml_serializer

??ParseComponentName = absolute.path.to.xml_serializer.controllers.xml_serializer_controller.ComponentNameController.parse_xml_serializer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ def convert_xml_to_dict(self, message: str, **kwargs):
"""
self.response.set_value("dict", xmltodict.parse(message))

def convert_dict_to_xml(self, xml_dict: dict, **kwargs) -> str:
"""converts the provided dictionary to an xml string
Args:
xml_dict (dict): a dictionary parsed by xmltodict
"""
self.response.set_value("xml", xmltodict.unparse(xml_dict))

def convert_node_to_xml(self, node, **kwargs):
"""converts the provided node to an xml string
Expand All @@ -51,7 +59,7 @@ def _serialize_node(
Union[str, Element]: the original call to this method returns a string representing the xml
the Element is only returned in the case of recursive calls
"""
xml = Element(tag_name)
xml = node.xml
# handles text data within tag
if hasattr(node, "text"):
xml.text = node.text
Expand All @@ -61,7 +69,7 @@ def _serialize_node(
value = getattr(node, attribName)
if hasattr(value, "__dict__"):
tagElement = self._serialize_node(value, attribName, level=level + 1)
# TODO: modify so double underscores are handled differently
# T2ODO: modify so double underscores are handled differently
try:
if attribName[0] == "_":
tagElement.tag = "_" + tagElement.tag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from flask_httpauth import HTTPTokenAuth
from digitalpy.core.main.object_factory import ObjectFactory

from FreeTAKServer.services.rest_api_service.views.base_view import BaseView
from FreeTAKServer.services.rest_api_service.views.base_view_controller import BaseViewController
from FreeTAKServer.core.configuration.LoggingConstants import LoggingConstants
from FreeTAKServer.core.configuration.CreateLoggerController import CreateLoggerController
from FreeTAKServer.core.parsers.JsonController import JsonController
Expand All @@ -15,7 +15,7 @@
loggingConstants = LoggingConstants(log_name="FTS-ManageEmergencyView")
logger = CreateLoggerController("FTS-ManageEmergencyView", logging_constants=loggingConstants).getLogger()

class ManageEmergency(BaseView):
class ManageEmergency(BaseViewController):

def __init__(self, request, response, action_mapper, configuration) -> None:
endpoints = {
Expand Down
6 changes: 3 additions & 3 deletions FreeTAKServer/controllers/services/FTS.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,11 @@ def start_cot_service(self, FTSServiceStartupConfigObject):
self.TCPCoTService, ServiceTypes.TCPCOTSERVICE
)
self.FilterGroup.add_source(self.TCPCoTService, ServiceTypes.TCPCOTSERVICE)
print("CoTService started")
logger.info("CoT Service Started")
return 1
except Exception as e:
except Exception as ex:
logger.error(
"an exception has been thrown in CoT service startup " + str(e)
"an exception has been thrown in CoT service startup " + str(ex)
)
return -1

Expand Down
4 changes: 2 additions & 2 deletions FreeTAKServer/core/configuration/MainConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@

# the version information of the server (recommended to leave as default)

FTS_VERSION = "FreeTAKServer-2.0.21"
FTS_VERSION = "FreeTAKServer-2.0.66"
API_VERSION = "3.0"
ROOTPATH = "/"
MAINPATH = Path(__file__).parent.parent.parent
USERPATH = rf"{ROOTPATH}usr/local/lib/"
PERSISTENCE_PATH = r'C:\Users\Natha Paquette\work\FreeTakServer\persistence'
PERSISTENCE_PATH = r'/opt/fts'

class MainConfig:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ def __init__(self):
self.LISTEN_COUNT = 2000
# ssl socket timeout
self.SSL_SOCK_TIMEOUT = 60
self.MAX_RECEPTION_ITERATIONS = 4
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

DELETE_GEO_OBJECT = "deletegeoobject"

BASE_OBJECT = "base_object"

CONFIGURATION_PATH_TEMPLATE = Template(
str(
pathlib.PurePath(
Expand Down
Loading

0 comments on commit 9fff5a3

Please sign in to comment.