smia 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. smia/__init__.py +106 -0
  2. smia/aas_model/__init__.py +2 -0
  3. smia/aas_model/aas_model_utils.py +195 -0
  4. smia/aas_model/extended_aas.py +54 -0
  5. smia/aas_model/extended_aas_model.py +531 -0
  6. smia/aas_model/extended_base.py +115 -0
  7. smia/aas_model/extended_concept_description.py +19 -0
  8. smia/aas_model/extended_submodel.py +580 -0
  9. smia/agents/__init__.py +3 -0
  10. smia/agents/extensible_smia_agent.py +109 -0
  11. smia/agents/smia_agent.py +220 -0
  12. smia/agents/smia_app_agent.py +39 -0
  13. smia/agents/smia_resource_agent.py +43 -0
  14. smia/assetconnection/__init__.py +1 -0
  15. smia/assetconnection/asset_connection.py +369 -0
  16. smia/assetconnection/http_asset_connection.py +259 -0
  17. smia/behaviours/__init__.py +3 -0
  18. smia/behaviours/aas_fsm_behaviour.py +19 -0
  19. smia/behaviours/acl_handling_behaviour.py +114 -0
  20. smia/behaviours/check_physical_asset_behaviour.py +33 -0
  21. smia/behaviours/end_behaviour.py +32 -0
  22. smia/behaviours/idle_behaviour.py +34 -0
  23. smia/behaviours/init_aas_archive_behaviour.py +41 -0
  24. smia/behaviours/init_aas_model_behaviour.py +425 -0
  25. smia/behaviours/negotiating_behaviour.py +212 -0
  26. smia/behaviours/specific_handle_behaviours/__init__.py +4 -0
  27. smia/behaviours/specific_handle_behaviours/handle_capability_behaviour.py +469 -0
  28. smia/behaviours/specific_handle_behaviours/handle_negotiation_behaviour.py +257 -0
  29. smia/behaviours/specific_handle_behaviours/handle_svc_request_behaviour.py +265 -0
  30. smia/behaviours/specific_handle_behaviours/handle_svc_response_behaviour.py +128 -0
  31. smia/css_ontology/__init__.py +2 -0
  32. smia/css_ontology/capability_skill_module.py +353 -0
  33. smia/css_ontology/capability_skill_ontology.py +189 -0
  34. smia/css_ontology/css_ontology_utils.py +277 -0
  35. smia/launchers/__init__.py +3 -0
  36. smia/launchers/smia_cli_starter.py +66 -0
  37. smia/launchers/smia_docker_starter.py +37 -0
  38. smia/launchers/smia_starter.py +49 -0
  39. smia/logic/__init__.py +3 -0
  40. smia/logic/agent_services.py +106 -0
  41. smia/logic/exceptions.py +213 -0
  42. smia/logic/inter_aas_interactions_utils.py +96 -0
  43. smia/logic/negotiation_utils.py +202 -0
  44. smia/logic/services_utils.py +105 -0
  45. smia/states/__init__.py +3 -0
  46. smia/states/state_booting.py +49 -0
  47. smia/states/state_booting_resource.py +34 -0
  48. smia/states/state_idle.py +25 -0
  49. smia/states/state_running.py +85 -0
  50. smia/states/state_stopping.py +27 -0
  51. smia/utilities/__init__.py +6 -0
  52. smia/utilities/aas_model_extension_utils.py +63 -0
  53. smia/utilities/fipa_acl_info.py +101 -0
  54. smia/utilities/general_utils.py +307 -0
  55. smia/utilities/properties_file_utils.py +240 -0
  56. smia/utilities/smia_archive_utils.py +393 -0
  57. smia/utilities/smia_general_info.py +61 -0
  58. smia/utilities/smia_info.py +85 -0
  59. smia-0.2.2.dist-info/LICENSE +674 -0
  60. smia-0.2.2.dist-info/METADATA +209 -0
  61. smia-0.2.2.dist-info/RECORD +63 -0
  62. smia-0.2.2.dist-info/WHEEL +5 -0
  63. smia-0.2.2.dist-info/top_level.txt +1 -0
smia/__init__.py ADDED
@@ -0,0 +1,106 @@
1
+ """
2
+ This is the main package of SMIA, that includes all source code and all subpackages.
3
+ """
4
+
5
+ __author__ = """Ekaitz Hurtado"""
6
+ __email__ = "ekaitz.hurtado@ehu.eus"
7
+ __version__ = '0.2.1'
8
+
9
+ __all__ = ['launchers', 'agents', 'utilities', 'assetconnection']
10
+
11
+ import logging
12
+ import ntpath
13
+
14
+ import spade
15
+
16
+ from .aas_model.aas_model_utils import AASModelUtils
17
+ from .logic.exceptions import CriticalError
18
+ from .utilities.aas_model_extension_utils import AASModelExtensionUtils
19
+ from .utilities import properties_file_utils, smia_archive_utils
20
+ from .utilities.general_utils import GeneralUtils
21
+ from .utilities.smia_general_info import SMIAGeneralInfo
22
+
23
+
24
+ def initial_self_configuration():
25
+ """
26
+ This method executes the initial configuration of the SMIA software.
27
+ """
28
+ # First, the BaSyx Python SDK is extended to have all new methods available
29
+ AASModelExtensionUtils.extend_basyx_aas_model()
30
+
31
+ # Initialize SMIA archive
32
+ smia_archive_utils.initialize_smia_archive()
33
+
34
+ # Configure logging
35
+ GeneralUtils.configure_logging()
36
+
37
+
38
+ def run(agent_object):
39
+ """
40
+ This method runs the SMIA software with a given agent.
41
+ """
42
+
43
+ _logger = logging.getLogger(__name__)
44
+
45
+ if agent_object is None:
46
+ _logger.error('To launch SMIA, an agent must be passed to the method "smia.run".')
47
+ return
48
+
49
+ async def main():
50
+ await agent_object.start()
51
+
52
+ await spade.wait_until_finished(agent_object)
53
+
54
+ # In the general properties file you can select the web interface (provided by SPADE).
55
+ web_ui = properties_file_utils.get_dt_general_property('web-ui')
56
+ if web_ui.lower() in ('yes', 'true', 't', '1'):
57
+ # bool(string) cannot be used as it is true as long as the string is not empty.
58
+ agent_object.web.start(hostname="0.0.0.0", port="10002")
59
+
60
+ spade.run(main())
61
+
62
+
63
+ def load_aas_model(file_path):
64
+ """
65
+ This method loads the AAS model using a given path to the AASX package file.
66
+
67
+ Args:
68
+ file_path (str): path to the AASX package file.
69
+ """
70
+ # If the user has not run the initial self-configuration method, it is now executed
71
+ initial_self_configuration()
72
+
73
+ _logger = logging.getLogger(__name__)
74
+ # TODO At the moment it only collects models in AASX, think about whether to leave option to XML and JSON as well.
75
+
76
+ if file_path is None:
77
+ _logger.error("The file path to the AAS model is None, so it cannot be loaded.")
78
+ return
79
+
80
+ # The variable with the AAS model file name is updated
81
+ aas_model_file_name = ntpath.split(file_path)[1] or ntpath.basename(ntpath.split(file_path)[0])
82
+ # SMIAGeneralInfo.CM_AAS_MODEL_FILENAME = aas_model_file_name
83
+ GeneralUtils.update_aas_model(aas_model_file_name)
84
+
85
+ # The file will be copied into the SMIA archive
86
+ try:
87
+ smia_archive_utils.copy_file_into_archive(file_path, SMIAGeneralInfo.CONFIGURATION_AAS_FOLDER_PATH)
88
+ except Exception as e:
89
+ raise CriticalError('It is not possible to copy the specified AAS model into the SMIA Archive, so the SMIA '
90
+ 'cannot be started. Reason: {}'.format(e))
91
+ _logger.info("AAS model {} copied to the SMIA Archive.".format(SMIAGeneralInfo.CM_AAS_MODEL_FILENAME))
92
+
93
+ # When the AAS model is inside the SMIA archive, it will be checked if it is valid
94
+ try:
95
+ config_file_path = AASModelUtils.get_configuration_file_path_from_standard_submodel()
96
+ init_config_file_name = ntpath.split(config_file_path)[1] or ntpath.basename(ntpath.split(config_file_path)[0])
97
+ config_file_bytes = AASModelUtils.get_file_bytes_from_aasx_by_path(config_file_path)
98
+ properties_file_utils.update_properties_file_by_bytes(config_file_bytes)
99
+ # with open(SMIAGeneralInfo.CONFIGURATION_FOLDER_PATH + '/' + init_config_file_name, "wb") as binary_file:
100
+ # binary_file.write(config_file_bytes) # Write bytes to file
101
+ # SMIAGeneralInfo.CM_GENERAL_PROPERTIES_FILENAME = init_config_file_name
102
+ except Exception as e:
103
+ _logger.warning("The AAS model does not contain the initialization configuration file. Make sure that it is "
104
+ "not necessary.")
105
+
106
+ # _logger.error(e)
@@ -0,0 +1,2 @@
1
+ """This module contains the extended model of Basyx Python SDK. This model is extended defining new methods for some
2
+ classes."""
@@ -0,0 +1,195 @@
1
+ import logging
2
+ from os import path
3
+
4
+ import basyx
5
+ from basyx.aas import model
6
+ from basyx.aas.adapter import aasx
7
+ from basyx.aas.util import traversal
8
+
9
+ from smia.logic.exceptions import CriticalError, AASModelReadingError
10
+ from smia.utilities import properties_file_utils
11
+ from smia.utilities.smia_general_info import SMIAGeneralInfo
12
+
13
+ _logger = logging.getLogger(__name__)
14
+
15
+
16
+ class AASModelUtils:
17
+ """This class contains utility methods related to the AAS model."""
18
+
19
+ @staticmethod
20
+ def read_aas_model_object_store():
21
+ """
22
+ This method reads the AAS model according to the selected serialization format.
23
+
24
+ Returns:
25
+ basyx.aas.model.DictObjectStore: object with all Python elements of the AAS model.
26
+ """
27
+ object_store = None
28
+ aas_model_file_path = properties_file_utils.get_aas_model_filepath()
29
+ aas_model_file_name, aas_model_file_extension = path.splitext(SMIAGeneralInfo.CM_AAS_MODEL_FILENAME)
30
+ try:
31
+ # The AAS model is read depending on the serialization format (extension of the AAS model file)
32
+ if aas_model_file_extension == '.json':
33
+ object_store = basyx.aas.adapter.json.read_aas_json_file(aas_model_file_path)
34
+ elif aas_model_file_extension == '.xml':
35
+ object_store = basyx.aas.adapter.xml.read_aas_xml_file(aas_model_file_path)
36
+ elif aas_model_file_extension == '.aasx':
37
+ with aasx.AASXReader(aas_model_file_path) as reader:
38
+ # Read all contained AAS objects and all referenced auxiliary files
39
+ object_store = model.DictObjectStore()
40
+ suppl_file_store = aasx.DictSupplementaryFileContainer()
41
+ reader.read_into(object_store=object_store,
42
+ file_store=suppl_file_store)
43
+ except ValueError as e:
44
+ _logger.error("Failed to read AAS model: invalid file.")
45
+ _logger.error(e)
46
+ raise CriticalError("Failed to read AAS model: invalid file.")
47
+ if object_store is None or len(object_store) == 0:
48
+ raise CriticalError("The AAS model is not valid. It is not possible to read and obtain elements of the AAS "
49
+ "metamodel.")
50
+ else:
51
+ return object_store
52
+
53
+ # Methods related to AASX Package
54
+ # -------------------------------
55
+ @staticmethod
56
+ def get_file_bytes_from_aasx_by_path(file_path):
57
+ """
58
+ This method gets the bytes of a file appended inside an AASX package by a given internal path.
59
+
60
+ Args:
61
+ file_path (str): the internal path of the file within the AASX package.
62
+
63
+ Returns:
64
+ obj: the content of the file in the form of bytes.
65
+ """
66
+ with aasx.AASXReader(properties_file_utils.get_aas_model_filepath()) as aasx_reader:
67
+ for part_name, content_type in aasx_reader.reader.list_parts():
68
+ if part_name == file_path:
69
+ return aasx_reader.reader.open_part(part_name).read()
70
+ else:
71
+ _logger.warning("The file with path {} does not find within the AASX Package.".format(file_path))
72
+ return None
73
+
74
+ # Methods related to the AAS model
75
+ # --------------------------------
76
+ @staticmethod
77
+ def get_configuration_file_path_from_standard_submodel():
78
+ """
79
+ This method gets the configuration file defined in the 'Software Nameplate' submodel, used as standard submodel
80
+ for the SMIA software definition.
81
+
82
+ Returns:
83
+ str: path inside the AASX package of the configuration file.
84
+ """
85
+ # First, the AAS model need to be read
86
+ object_store = AASModelUtils.read_aas_model_object_store()
87
+ soft_nameplate_config_paths = AASModelUtils.get_elem_of_software_nameplate_by_semantic_id(object_store)
88
+ if soft_nameplate_config_paths is None:
89
+ raise CriticalError(
90
+ "Configuration of SMIA is required and it is not defined within the Software Nameplate submodel.")
91
+ for config_path_elem in soft_nameplate_config_paths:
92
+ sm_elem = config_path_elem.get_sm_element_by_semantic_id(
93
+ AASModelInfo.SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_TYPE)
94
+ if (sm_elem is not None) and (sm_elem.value == 'initial configuration'):
95
+ return config_path_elem.get_sm_element_by_semantic_id(
96
+ AASModelInfo.SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_URI).value
97
+ return None
98
+
99
+ @staticmethod
100
+ def get_elem_of_software_nameplate_by_semantic_id(object_store):
101
+ """
102
+ This method obtains a SubmodelElement of the Software Nameplate submodel.
103
+
104
+ Args:
105
+ object_store (basyx.aas.model.DictObjectStore): storage with all AAS information,
106
+
107
+ Returns:
108
+ basyx.aas.model.SubmodelElement: submodelElement with the given semanticID
109
+ """
110
+ for object in object_store:
111
+ if isinstance(object, basyx.aas.model.Submodel):
112
+ if object.check_semantic_id_exist(AASModelInfo.SEMANTICID_SOFTWARE_NAMEPLATE_SUBMODEL):
113
+ for elem in traversal.walk_submodel(object):
114
+ if isinstance(elem, basyx.aas.model.SubmodelElement):
115
+ if elem.check_semantic_id_exist(AASModelInfo.SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_PATHS):
116
+ return elem
117
+ return None
118
+
119
+ @staticmethod
120
+ async def get_key_type_by_string(key_type_string):
121
+ """
122
+ This method gets the KeyType defined in BaSyx SDK related to the given string.
123
+
124
+ Args:
125
+ key_type_string (str): string of the desired KeyType.
126
+
127
+ Returns:
128
+ basyx.aas.model.KeyTypes: object of the KeyType defined in BaSyx.
129
+ """
130
+ try:
131
+ return getattr(basyx.aas.model.KeyTypes, key_type_string)
132
+ except AttributeError as e:
133
+ _logger.error(e)
134
+ raise AASModelReadingError("The KeyType with string {} does not exist in the AAS model defined"
135
+ " types".format(key_type_string), sme_class=None, reason='KeyTypeAttributeError')
136
+
137
+ @staticmethod
138
+ async def get_model_type_by_key_type(key_type):
139
+ """
140
+ This method gets the AAS model class by a given KeyType defined in BaSyx SDK.
141
+
142
+ Args:
143
+ key_type (basyx.aas.model.KeyTypes): desired KeyType.
144
+
145
+ Returns:
146
+ object of the AAS model class.
147
+ """
148
+ for model_class, key_type_class in basyx.aas.model.KEY_TYPES_CLASSES.items():
149
+ if key_type_class == key_type:
150
+ return model_class
151
+ return None
152
+
153
+ @staticmethod
154
+ async def create_aas_reference_object(reference_type, keys_dict=None, external_ref=None):
155
+ """
156
+ This method creates the AAS BaSyx Reference Python object. Depending on the reference type to create (ModelReference or ExternalReference), some information is required. If a Reference cannot be created, it returns None.
157
+
158
+ Args:
159
+ reference_type (str): type of the reference to be created (ModelReference or ExternalReference).
160
+ keys_dict (list): if ModelReference is selected, the required keys information in form of a JSON array.
161
+ external_ref (str): if ExternalReference is selected, the required globally unique identifier.
162
+
163
+ Returns:
164
+ basyx.aas.model.Reference: BaSyx Python object of the AAS Reference.
165
+ """
166
+ ref_object = None
167
+ if 'ModelReference' == reference_type:
168
+ keys = ()
169
+ last_type = None
170
+ for key in keys_dict:
171
+ basyx_key_type = await AASModelUtils.get_key_type_by_string(key['type'])
172
+ keys += (model.Key(basyx_key_type, key['value']),)
173
+ last_type = basyx_key_type
174
+ ref_object = basyx.aas.model.ModelReference(
175
+ key=keys, type_=await AASModelUtils.get_model_type_by_key_type(last_type))
176
+
177
+ elif 'ExternalReference' == reference_type:
178
+ ref_object = model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
179
+ value=external_ref),))
180
+ return ref_object
181
+
182
+
183
+ class AASModelInfo:
184
+ """This class contains the information related to AAS model."""
185
+ SEMANTICID_SOFTWARE_NAMEPLATE_SUBMODEL = 'https://admin-shell.io/idta/SoftwareNameplate/1/0'
186
+ SEMANTICID_SOFTWARE_NAMEPLATE_INSTANCE_NAME = ('https://admin-shell.io/idta/SoftwareNameplate/1/0/SoftwareNameplate'
187
+ '/SoftwareNameplateInstance/InstanceName')
188
+ SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_PATHS = ('https://admin-shell.io/idta/SoftwareNameplate/1/0'
189
+ '/SoftwareNameplate/SoftwareNameplateInstance/ConfigurationPaths')
190
+ SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_TYPE = ('https://admin-shell.io/idta/SoftwareNameplate/1/0/SoftwareNameplate'
191
+ '/SoftwareNameplateInstance/ConfigurationType')
192
+ SEMANTIC_ID_SOFTWARE_NAMEPLATE_CONFIG_URI = ('https://admin-shell.io/idta/SoftwareNameplate/1/0/SoftwareNameplate'
193
+ '/SoftwareNameplateInstance/ConfigurationURI')
194
+
195
+ # TODO pasar aqui todos los IDs requeridos en el AAS (p.e el de AID submodel)
@@ -0,0 +1,54 @@
1
+ class ExtendedAssetAdministrationShell:
2
+ """This class contains methods to be added to AssetAdministrationShell class of Basyx Python SDK model."""
3
+
4
+ def print_aas_information(self):
5
+ print("AAS information:")
6
+ print("\tid: " + self.id)
7
+ print("\tid_short: " + self.id_short)
8
+ print("\tdisplayName:{}".format(self.display_name))
9
+ print("\tdescription:{}".format(self.description))
10
+ print("\tcategory: {}".format(self.category))
11
+ print("\tderivedFrom: {}".format(self.derived_from))
12
+ print("\tadministration: {}".format(ExtendedGeneralMethods.print_administration(self.administration)))
13
+ print("\textension: " + "{}".format(ExtendedGeneralMethods.print_namespace_set(self.extension)))
14
+ print("\tdataSpecifications: " + "{}".format(
15
+ ExtendedGeneralMethods.print_data_specifications(self.embedded_data_specifications)))
16
+
17
+
18
+ class ExtendedAssetInformation:
19
+ """This class contains methods to be added to AssetInformation class of Basyx Python SDK model."""
20
+
21
+ def print_asset_information(self):
22
+ print("Asset information:")
23
+ print("\tassetKind: {}".format(self.asset_kind))
24
+ print("\tassetType: {}".format(self.asset_type))
25
+ print("\tspecificAssetId: {}".format(self.specific_asset_id))
26
+ print("\tglobalAssetId: {}".format(self.global_asset_id))
27
+ print("\tdefaultThumbnail: {}".format(self.default_thumbnail))
28
+
29
+
30
+ class ExtendedGeneralMethods:
31
+
32
+ @staticmethod
33
+ def print_administration(administration):
34
+ if administration:
35
+ return "version[{}".format(administration.version) + "], revision[{}".format(
36
+ administration.revision) + "], creator[{}".format(administration.creator) + "], templateId[{}".format(
37
+ administration.template_id) + "]"
38
+ else:
39
+ return ""
40
+
41
+ @staticmethod
42
+ def print_namespace_set(namespace_set):
43
+ string = ""
44
+ for item in namespace_set:
45
+ string += str(item) + ","
46
+ return string
47
+
48
+ @staticmethod
49
+ def print_data_specifications(embedded_data_specifications):
50
+ string = ""
51
+ for item in embedded_data_specifications:
52
+ string += ("(Reference: {}".format(item.data_specification) +
53
+ " | Content: {}".format(item.data_specification_content) + "),")
54
+ return string