holado 0.2.2__py3-none-any.whl → 0.2.4__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.
Potentially problematic release.
This version of holado might be problematic. Click here for more details.
- holado/__init__.py +11 -1
- {holado-0.2.2.dist-info → holado-0.2.4.dist-info}/METADATA +13 -9
- holado-0.2.4.dist-info/RECORD +535 -0
- holado_ais/__init__.py +33 -0
- holado_ais/ais/MaritimeIdentificationDigits.csv +295 -0
- holado_ais/ais/ais_manager.py +151 -0
- holado_ais/ais/ais_messages.py +204 -0
- holado_ais/ais/ais_payload.py +35 -0
- holado_ais/ais/enums.py +37 -0
- holado_ais/ais/patch_pyais.py +1303 -0
- holado_ais/tests/behave/steps/__init__.py +17 -0
- holado_ais/tests/behave/steps/ais/__init__.py +0 -0
- holado_ais/tests/behave/steps/ais/ais_manager_steps.py +50 -0
- holado_ais/tests/behave/steps/ais/ais_messages_steps.py +182 -0
- holado_binary/__init__.py +17 -0
- holado_binary/ipc/binary.py +125 -0
- holado_binary/ipc/bit_series.py +307 -0
- holado_binary/tests/behave/steps/__init__.py +17 -0
- holado_binary/tests/behave/steps/ipc/binary_steps.py +57 -0
- holado_binary/tests/behave/steps/ipc/bit_series_steps.py +131 -0
- holado_context/__init__.py +16 -0
- holado_context/tests/behave/steps/__init__.py +16 -0
- holado_context/tests/behave/steps/private/__init__.py +16 -0
- holado_context/tests/behave/steps/private/common/context_steps.py +36 -0
- holado_core/__init__.py +32 -0
- holado_core/common/__init__.py +0 -0
- holado_core/common/actors/actions.py +97 -0
- holado_core/common/actors/actor.py +226 -0
- holado_core/common/actors/element_actor.py +32 -0
- holado_core/common/actors/find_actor.py +106 -0
- holado_core/common/actors/tree_actor.py +32 -0
- holado_core/common/actors/verify_actions.py +69 -0
- holado_core/common/block/base.py +122 -0
- holado_core/common/block/block_manager.py +173 -0
- holado_core/common/block/block_method.py +46 -0
- holado_core/common/block/block_steps.py +37 -0
- holado_core/common/block/function.py +42 -0
- holado_core/common/block/scope_function.py +28 -0
- holado_core/common/block/scope_manager.py +238 -0
- holado_core/common/block/scope_steps.py +141 -0
- holado_core/common/criterias/and_criteria.py +61 -0
- holado_core/common/criterias/criteria.py +78 -0
- holado_core/common/criterias/or_criteria.py +64 -0
- holado_core/common/criterias/tools/criteria_context.py +20 -0
- holado_core/common/criterias/tools/criteria_parameters.py +18 -0
- holado_core/common/drivers/driver.py +153 -0
- holado_core/common/drivers/element_driver.py +30 -0
- holado_core/common/drivers/element_internal_api.py +239 -0
- holado_core/common/drivers/internal_api.py +40 -0
- holado_core/common/drivers/tree_driver.py +30 -0
- holado_core/common/drivers/tree_internal_api.py +176 -0
- holado_core/common/exceptions/__init__.py +0 -0
- holado_core/common/exceptions/element_exception.py +28 -0
- holado_core/common/exceptions/exceptions.py +24 -0
- holado_core/common/exceptions/functional_exception.py +21 -0
- holado_core/common/exceptions/holado_exception.py +25 -0
- holado_core/common/exceptions/technical_exception.py +27 -0
- holado_core/common/exceptions/timeout_exception.py +20 -0
- holado_core/common/exceptions/verify_exception.py +20 -0
- holado_core/common/finders/after_in_tree_finder.py +87 -0
- holado_core/common/finders/element_finder.py +60 -0
- holado_core/common/finders/else_finder.py +105 -0
- holado_core/common/finders/finder.py +478 -0
- holado_core/common/finders/or_finder.py +98 -0
- holado_core/common/finders/then_finder.py +157 -0
- holado_core/common/finders/tools/enums.py +30 -0
- holado_core/common/finders/tools/find_builder.py +118 -0
- holado_core/common/finders/tools/find_context.py +405 -0
- holado_core/common/finders/tools/find_info.py +27 -0
- holado_core/common/finders/tools/find_parameters.py +240 -0
- holado_core/common/finders/tools/find_updater.py +95 -0
- holado_core/common/finders/tools/finder_info.py +26 -0
- holado_core/common/finders/tree_finder.py +146 -0
- holado_core/common/handlers/__init__.py +0 -0
- holado_core/common/handlers/abstracts/base_redo.py +695 -0
- holado_core/common/handlers/abstracts/get_or_create.py +120 -0
- holado_core/common/handlers/element_holder.py +122 -0
- holado_core/common/handlers/enums.py +23 -0
- holado_core/common/handlers/exceptions/redo_exceptions.py +28 -0
- holado_core/common/handlers/features/resource_by_name.py +187 -0
- holado_core/common/handlers/features/resource_by_type.py +174 -0
- holado_core/common/handlers/redo.py +119 -0
- holado_core/common/handlers/wait.py +127 -0
- holado_core/common/inspectors/element_inspector.py +57 -0
- holado_core/common/inspectors/inspector.py +221 -0
- holado_core/common/inspectors/tools/inspect_builder.py +169 -0
- holado_core/common/inspectors/tools/inspect_context.py +69 -0
- holado_core/common/inspectors/tools/inspect_parameters.py +181 -0
- holado_core/common/inspectors/tree_inspector.py +73 -0
- holado_core/common/resource/persisted_data_manager.py +113 -0
- holado_core/common/resource/persisted_method_to_call_manager.py +133 -0
- holado_core/common/resource/resource_manager.py +143 -0
- holado_core/common/tables/__init__.py +1 -0
- holado_core/common/tables/comparators/__init__.py +0 -0
- holado_core/common/tables/comparators/boolean_table_cell_comparator.py +25 -0
- holado_core/common/tables/comparators/bytes_table_cell_comparator.py +25 -0
- holado_core/common/tables/comparators/datetime_table_cell_comparator.py +24 -0
- holado_core/common/tables/comparators/float_table_cell_comparator.py +31 -0
- holado_core/common/tables/comparators/integer_table_cell_comparator.py +25 -0
- holado_core/common/tables/comparators/internal_table_cell_comparator.py +30 -0
- holado_core/common/tables/comparators/string_table_cell_comparator.py +24 -0
- holado_core/common/tables/comparators/string_table_comparator.py +29 -0
- holado_core/common/tables/comparators/string_table_row_comparator.py +29 -0
- holado_core/common/tables/comparators/table_cell_comparator.py +40 -0
- holado_core/common/tables/comparators/table_comparator.py +209 -0
- holado_core/common/tables/comparators/table_comparator_manager.py +60 -0
- holado_core/common/tables/comparators/table_row_comparator.py +116 -0
- holado_core/common/tables/comparators/table_with_header_comparator.py +68 -0
- holado_core/common/tables/converters/__init__.py +0 -0
- holado_core/common/tables/converters/table_converter.py +194 -0
- holado_core/common/tables/enums.py +23 -0
- holado_core/common/tables/table.py +261 -0
- holado_core/common/tables/table_cell.py +126 -0
- holado_core/common/tables/table_manager.py +366 -0
- holado_core/common/tables/table_row.py +169 -0
- holado_core/common/tables/table_with_header.py +236 -0
- holado_core/common/tools/__init__.py +0 -0
- holado_core/common/tools/comparators/comparator.py +151 -0
- holado_core/common/tools/comparators/object_comparator.py +21 -0
- holado_core/common/tools/converters/converter.py +107 -0
- holado_core/common/tools/path_manager.py +185 -0
- holado_core/common/tools/string_tools.py +135 -0
- holado_core/common/tools/tools.py +172 -0
- holado_core/common/transport/__init__.py +0 -0
- holado_core/common/transport/crc.py +40 -0
- holado_core/tests/behave/steps/__init__.py +20 -0
- holado_core/tests/behave/steps/common/__init__.py +0 -0
- holado_core/tests/behave/steps/common/common_steps.py +136 -0
- holado_core/tests/behave/steps/common/config_steps.py +42 -0
- holado_core/tests/behave/steps/common/resource_steps.py +62 -0
- holado_core/tests/behave/steps/common/tables_steps.py +537 -0
- holado_core/tools/__init__.py +0 -0
- holado_core/tools/abstracts/__init__.py +0 -0
- holado_core/tools/abstracts/blocking_command_service.py +56 -0
- holado_core/tools/abstracts/service.py +48 -0
- holado_data/__init__.py +31 -0
- holado_data/data/generator/base.py +93 -0
- holado_data/data/generator/python_generator.py +30 -0
- holado_data/tests/behave/steps/__init__.py +17 -0
- holado_data/tests/behave/steps/data/generator_steps.py +91 -0
- holado_data/tests/behave/steps/tools/utils_steps.py +60 -0
- holado_db/__init__.py +32 -0
- holado_db/tests/behave/steps/__init__.py +18 -0
- holado_db/tests/behave/steps/tools/db/db_client_steps.py +134 -0
- holado_db/tests/behave/steps/tools/db/postgresql_client_steps.py +59 -0
- holado_db/tests/behave/steps/tools/db/sqlite_client_steps.py +57 -0
- holado_db/tools/db/clients/base/db_client.py +206 -0
- holado_db/tools/db/clients/postgresql/postgresql_client.py +65 -0
- holado_db/tools/db/clients/sqlite/sqlite_client.py +56 -0
- holado_db/tools/db/db_manager.py +109 -0
- holado_db/tools/db/query/base/query_builder.py +87 -0
- holado_db/tools/db/query/pypika/pypika_query_builder.py +193 -0
- holado_db/tools/db/query/query_manager.py +77 -0
- holado_docker/__init__.py +25 -0
- holado_docker/sdk/docker/docker_client.py +268 -0
- holado_docker/sdk/docker/docker_service.py +71 -0
- holado_docker/tests/behave/steps/__init__.py +16 -0
- holado_docker/tests/behave/steps/tools/docker_steps.py +33 -0
- holado_docker/tools/docker_controler/__init__.py +26 -0
- holado_docker/tools/docker_controler/docker_controler_client.py +36 -0
- holado_docker/tools/docker_controler/docker_controler_server.py +31 -0
- holado_docker/tools/docker_controler/proto/compile_proto.py +60 -0
- holado_docker/tools/docker_controler/proto/definitions/docker_controler.proto +63 -0
- holado_docker/tools/docker_controler/proto/generated/docker_controler_pb2.py +52 -0
- holado_docker/tools/docker_controler/proto/generated/docker_controler_pb2_grpc.py +233 -0
- holado_grpc/__init__.py +32 -0
- holado_grpc/api/rpc/TODO.txt +4 -0
- holado_grpc/api/rpc/grpc_client.py +181 -0
- holado_grpc/api/rpc/grpc_manager.py +81 -0
- holado_grpc/ipc/rpc/__init__.py +0 -0
- holado_grpc/ipc/rpc/grpc_compiler.py +45 -0
- holado_grpc/ipc/rpc/grpc_services.py +165 -0
- holado_grpc/tests/behave/steps/__init__.py +16 -0
- holado_grpc/tests/behave/steps/api/grpc_client_steps.py +173 -0
- holado_grpc/tests/behave/steps/private/__init__.py +16 -0
- holado_grpc/tests/behave/steps/private/api/grpc_steps.py +77 -0
- holado_helper/__init__.py +35 -0
- holado_helper/debug/README.txt +32 -0
- holado_helper/debug/memory/memory_profiler.py +106 -0
- holado_helper/docker/init_user.sh +24 -0
- holado_helper/docker/logging.conf +42 -0
- holado_helper/docker/run_holado_test_nonreg_in_docker.sh +120 -0
- holado_helper/docker/run_terminal_in_docker-with_docker_control.sh +103 -0
- holado_helper/docker/run_terminal_in_docker.sh +101 -0
- holado_helper/holado_module_template/__init__.py +31 -0
- holado_helper/holado_module_template/test/behave/steps/__init__.py +16 -0
- holado_helper/holado_module_template/test/behave/steps/private/__init__.py +16 -0
- holado_helper/script/action.py +109 -0
- holado_helper/script/action_script.py +477 -0
- holado_helper/script/any_action_script.py +126 -0
- holado_helper/script/behave_action_script.py +99 -0
- holado_helper/script/csv_action_script.py +142 -0
- holado_helper/script/initialize_script.py +115 -0
- holado_helper/script/input_output_script.py +136 -0
- holado_helper/script/job.py +75 -0
- holado_helper/script/json_action_script.py +104 -0
- holado_helper/script/script.py +110 -0
- holado_json/__init__.py +16 -0
- holado_json/ipc/json.py +125 -0
- holado_json/ipc/json_converter.py +69 -0
- holado_json/ipc/json_types.py +183 -0
- holado_json/tests/behave/steps/__init__.py +16 -0
- holado_json/tests/behave/steps/ipc/__init__.py +0 -0
- holado_json/tests/behave/steps/ipc/json_steps.py +120 -0
- holado_keycloak/__init__.py +16 -0
- holado_keycloak/tests/behave/steps/__init__.py +16 -0
- holado_keycloak/tests/behave/steps/tools/keycloak_client_steps.py +59 -0
- holado_keycloak/tools/keycloak/__init__.py +0 -0
- holado_keycloak/tools/keycloak/keycloak_client.py +61 -0
- holado_logging/__init__.py +37 -0
- holado_logging/common/logging/holado_logger.py +75 -0
- holado_logging/common/logging/log_config.py +128 -0
- holado_logging/common/logging/log_manager.py +292 -0
- holado_multitask/__init__.py +33 -0
- holado_multitask/multiprocessing/context/process_context.py +35 -0
- holado_multitask/multiprocessing/function_process.py +102 -0
- holado_multitask/multiprocessing/periodic_function_process.py +135 -0
- holado_multitask/multiprocessing/process.py +196 -0
- holado_multitask/multiprocessing/processesmanager.py +133 -0
- holado_multitask/multitasking/multitask_manager.py +439 -0
- holado_multitask/multithreading/__init__.py +0 -0
- holado_multitask/multithreading/context/thread_context.py +84 -0
- holado_multitask/multithreading/functionthreaded.py +129 -0
- holado_multitask/multithreading/loopfunctionthreaded.py +45 -0
- holado_multitask/multithreading/loopthread.py +100 -0
- holado_multitask/multithreading/periodicfunctionthreaded.py +136 -0
- holado_multitask/multithreading/reflection/inspect.py +47 -0
- holado_multitask/multithreading/reflection/sys.py +29 -0
- holado_multitask/multithreading/reflection/traceback.py +35 -0
- holado_multitask/multithreading/thread.py +177 -0
- holado_multitask/multithreading/threadsmanager.py +162 -0
- holado_multitask/multithreading/timer.py +48 -0
- holado_multitask/tests/behave/steps/__init__.py +17 -0
- holado_multitask/tests/behave/steps/multiprocessing_steps.py +138 -0
- holado_multitask/tests/behave/steps/multithreading_steps.py +129 -0
- holado_protobuf/__init__.py +61 -0
- holado_protobuf/ipc/protobuf/__init__.py +0 -0
- holado_protobuf/ipc/protobuf/abstracts/type.py +45 -0
- holado_protobuf/ipc/protobuf/protobuf_compiler.py +118 -0
- holado_protobuf/ipc/protobuf/protobuf_converter.py +153 -0
- holado_protobuf/ipc/protobuf/protobuf_messages.py +968 -0
- holado_protobuf/ipc/protobuf/protobuf_modifier.py +65 -0
- holado_protobuf/ipc/protobuf/types/__init__.py +0 -0
- holado_protobuf/ipc/protobuf/types/google/__init__.py +0 -0
- holado_protobuf/ipc/protobuf/types/google/protobuf.py +124 -0
- holado_protobuf/tests/behave/steps/__init__.py +16 -0
- holado_protobuf/tests/behave/steps/ipc/protobuf_steps.py +297 -0
- holado_python/__init__.py +35 -0
- holado_python/common/enums.py +34 -0
- holado_python/common/iterables.py +30 -0
- holado_python/common/tools/comparators/boolean_comparator.py +37 -0
- holado_python/common/tools/comparators/bytes_comparator.py +48 -0
- holado_python/common/tools/comparators/datetime_comparator.py +74 -0
- holado_python/common/tools/comparators/float_comparator.py +97 -0
- holado_python/common/tools/comparators/integer_comparator.py +37 -0
- holado_python/common/tools/comparators/string_comparator.py +99 -0
- holado_python/common/tools/comparators/type_comparator.py +31 -0
- holado_python/common/tools/datetime.py +279 -0
- holado_python/standard_library/csv.py +207 -0
- holado_python/standard_library/hashlib.py +82 -0
- holado_python/standard_library/multiprocessing.py +62 -0
- holado_python/standard_library/queue.py +79 -0
- holado_python/standard_library/socket/blocking_socket.py +136 -0
- holado_python/standard_library/socket/echo_server.py +28 -0
- holado_python/standard_library/socket/message_socket.py +91 -0
- holado_python/standard_library/socket/non_blocking_socket.py +151 -0
- holado_python/standard_library/socket/socket.py +148 -0
- holado_python/standard_library/ssl/resources/certificates/NOTES.txt +5 -0
- holado_python/standard_library/ssl/resources/certificates/localhost.crt +19 -0
- holado_python/standard_library/ssl/resources/certificates/localhost.key +28 -0
- holado_python/standard_library/ssl/ssl.py +81 -0
- holado_python/standard_library/typing.py +192 -0
- holado_python/tests/behave/steps/__init__.py +27 -0
- holado_python/tests/behave/steps/builtins/str_steps.py +45 -0
- holado_python/tests/behave/steps/convert_steps.py +59 -0
- holado_python/tests/behave/steps/iterable_steps.py +87 -0
- holado_python/tests/behave/steps/standard_library/csv_steps.py +134 -0
- holado_python/tests/behave/steps/standard_library/datetime_steps.py +163 -0
- holado_python/tests/behave/steps/standard_library/hashlib_steps.py +57 -0
- holado_python/tests/behave/steps/standard_library/multiprocessing_steps.py +56 -0
- holado_python/tests/behave/steps/standard_library/queue_steps.py +358 -0
- holado_python/tests/behave/steps/standard_library/socket_steps.py +258 -0
- holado_python/tests/behave/steps/standard_library/ssl_steps.py +67 -0
- holado_rabbitmq/__init__.py +28 -0
- holado_rabbitmq/tests/behave/steps/__init__.py +17 -0
- holado_rabbitmq/tests/behave/steps/tools/rabbitmq_client_steps.py +537 -0
- holado_rabbitmq/tests/behave/steps/tools/rabbitmq_server_steps.py +64 -0
- holado_rabbitmq/tools/rabbitmq/rabbitmq_blocking_client.py +311 -0
- holado_rabbitmq/tools/rabbitmq/rabbitmq_client.py +674 -0
- holado_rabbitmq/tools/rabbitmq/rabbitmq_manager.py +173 -0
- holado_rabbitmq/tools/rabbitmq/rabbitmq_select_client.py +428 -0
- holado_rabbitmq/tools/rabbitmq/rabbitmq_server.py +24 -0
- holado_redis/__init__.py +17 -0
- holado_redis/tests/behave/steps/__init__.py +16 -0
- holado_redis/tests/behave/steps/tools/redis_client_steps.py +101 -0
- holado_redis/tools/redis/TODO.txt +7 -0
- holado_redis/tools/redis/redis_client.py +190 -0
- holado_redis/tools/redis/redis_manager.py +38 -0
- holado_report/__init__.py +27 -0
- holado_report/report/analyze/execution_historic_manager.py +96 -0
- holado_report/report/analyze/scenario_duration_manager.py +245 -0
- holado_report/report/builders/detailed_scenario_failed_report_builder.py +146 -0
- holado_report/report/builders/json_execution_historic_report_builder.py +123 -0
- holado_report/report/builders/report_builder.py +64 -0
- holado_report/report/builders/short_scenario_failed_report_builder.py +76 -0
- holado_report/report/builders/summary_report_builder.py +89 -0
- holado_report/report/builders/summary_scenario_failed_report_builder.py +56 -0
- holado_report/report/builders/summary_scenario_report_builder.py +74 -0
- holado_report/report/execution_historic.py +141 -0
- holado_report/report/report_manager.py +256 -0
- holado_report/report/reports/base_report.py +163 -0
- holado_report/report/reports/feature_report.py +106 -0
- holado_report/report/reports/scenario_report.py +64 -0
- holado_rest/__init__.py +24 -0
- holado_rest/api/rest/TODO.txt +2 -0
- holado_rest/api/rest/rest_client.py +113 -0
- holado_rest/api/rest/rest_manager.py +62 -0
- holado_rest/tests/behave/steps/__init__.py +16 -0
- holado_rest/tests/behave/steps/api/__init__.py +0 -0
- holado_rest/tests/behave/steps/api/rest_client_steps.py +141 -0
- holado_rest/tests/behave/steps/private/__init__.py +16 -0
- holado_rest/tests/behave/steps/private/api/__init__.py +0 -0
- holado_rest/tests/behave/steps/private/api/rest_steps.py +70 -0
- holado_s3/__init__.py +17 -0
- holado_s3/tests/behave/steps/__init__.py +17 -0
- holado_s3/tests/behave/steps/private/__init__.py +16 -0
- holado_s3/tests/behave/steps/private/tools/s3_steps.py +89 -0
- holado_s3/tests/behave/steps/tools/s3_client_steps.py +403 -0
- holado_s3/tests/behave/steps/tools/s3_server_steps.py +57 -0
- holado_s3/tools/s3/__init__.py +0 -0
- holado_s3/tools/s3/boto3_s3_client.py +59 -0
- holado_s3/tools/s3/minio_client.py +75 -0
- holado_s3/tools/s3/moto_server.py +52 -0
- holado_scripting/__init__.py +53 -0
- holado_scripting/common/tools/dynamic_text_manager.py +73 -0
- holado_scripting/common/tools/evaluate_parameters.py +210 -0
- holado_scripting/common/tools/expression_evaluator.py +387 -0
- holado_scripting/common/tools/variable_manager.py +321 -0
- holado_scripting/tests/behave/steps/__init__.py +22 -0
- holado_scripting/tests/behave/steps/common/tools/variable_convert_steps.py +158 -0
- holado_scripting/tests/behave/steps/common/tools/variable_new_steps.py +130 -0
- holado_scripting/tests/behave/steps/common/tools/variable_steps.py +108 -0
- holado_scripting/tests/behave/steps/common/tools/variable_verify_steps.py +160 -0
- holado_scripting/tests/behave/steps/scenario/function_steps.py +77 -0
- holado_scripting/tests/behave/steps/scenario/if_steps.py +87 -0
- holado_scripting/tests/behave/steps/scenario/loop_steps.py +119 -0
- holado_scripting/text/base/base_function.py +25 -0
- holado_scripting/text/base/base_verify_function.py +25 -0
- holado_scripting/text/base/text_inspecter.py +204 -0
- holado_scripting/text/interpreter/exceptions/interpreter_exception.py +25 -0
- holado_scripting/text/interpreter/functions/function_cast.py +60 -0
- holado_scripting/text/interpreter/functions/function_convert.py +57 -0
- holado_scripting/text/interpreter/functions/function_dynamic_value.py +40 -0
- holado_scripting/text/interpreter/functions/function_escape_all_bytes.py +35 -0
- holado_scripting/text/interpreter/functions/function_exists_variable.py +39 -0
- holado_scripting/text/interpreter/functions/function_hex_to_bytes.py +49 -0
- holado_scripting/text/interpreter/functions/function_hex_to_int.py +53 -0
- holado_scripting/text/interpreter/functions/function_to_base_64.py +41 -0
- holado_scripting/text/interpreter/functions/function_to_bytes.py +50 -0
- holado_scripting/text/interpreter/functions/function_to_hex.py +42 -0
- holado_scripting/text/interpreter/text_interpreter.py +216 -0
- holado_scripting/text/verifier/exceptions/verifier_exception.py +21 -0
- holado_scripting/text/verifier/functions/verify_function_extract_in.py +35 -0
- holado_scripting/text/verifier/functions/verify_function_match_pattern.py +63 -0
- holado_scripting/text/verifier/text_verifier.py +103 -0
- holado_sftp/__init__.py +16 -0
- holado_sftp/tests/behave/steps/__init__.py +17 -0
- holado_sftp/tests/behave/steps/private/__init__.py +16 -0
- holado_sftp/tests/behave/steps/private/tools/sftp_steps.py +78 -0
- holado_sftp/tests/behave/steps/tools/sftp_client_steps.py +94 -0
- holado_sftp/tests/behave/steps/tools/sftp_server_steps.py +82 -0
- holado_sftp/tools/sftp/__init__.py +0 -0
- holado_sftp/tools/sftp/sftp_client.py +103 -0
- holado_sftp/tools/sftp/sftp_server.py +39 -0
- holado_swagger/__init__.py +31 -0
- holado_swagger/swagger_hub/mockserver/mockserver_client.py +82 -0
- holado_swagger/swagger_hub/mockserver/mockserver_manager.py +32 -0
- holado_swagger/tests/behave/steps/__init__.py +16 -0
- holado_swagger/tests/behave/steps/swagger_hub/mockserver_steps.py +74 -0
- holado_system/system/command/command.py +216 -0
- holado_system/system/command/command_result.py +128 -0
- holado_system/system/command/curl_command.py +101 -0
- holado_system/system/command/exceptions.py +59 -0
- holado_system/system/filesystem/file.py +76 -0
- holado_system/system/global_system.py +187 -0
- holado_system/tests/behave/steps/__init__.py +18 -0
- holado_system/tests/behave/steps/system/commands_steps.py +92 -0
- holado_system/tests/behave/steps/system/file_steps.py +215 -0
- holado_system/tests/behave/steps/system/system_steps.py +84 -0
- holado_test/__init__.py +27 -0
- holado_test/behave/__init__.py +0 -0
- holado_test/behave/behave.py +397 -0
- holado_test/behave/behave_environment.py +143 -0
- holado_test/behave/behave_function.py +33 -0
- holado_test/behave/behave_manager.py +555 -0
- holado_test/behave/independant_runner.py +68 -0
- holado_test/behave/scenario/__init__.py +0 -0
- holado_test/behave/scenario/behave_step_tools.py +157 -0
- holado_test/common/context/feature_context.py +79 -0
- holado_test/common/context/scenario_context.py +217 -0
- holado_test/common/context/step_context.py +66 -0
- holado_test/common/exceptions/undefined_step_exception.py +21 -0
- holado_test/scenario/step_tools.py +523 -0
- holado_test/scenario/tester_tools.py +52 -0
- holado_test/test_config.py +26 -0
- holado_test/tests/behave/steps/__init__.py +18 -0
- holado_test/tests/behave/steps/scenario/exception_steps.py +87 -0
- holado_test/tests/behave/steps/scenario/scenario_steps.py +87 -0
- holado_test/tests/behave/steps/scenario/tester_steps.py +65 -0
- holado_value/__init__.py +24 -0
- holado_value/common/tables/comparators/table_2_value_table_cell_comparator.py +195 -0
- holado_value/common/tables/comparators/table_2_value_table_comparator.py +27 -0
- holado_value/common/tables/comparators/table_2_value_table_row_comparator.py +27 -0
- holado_value/common/tables/comparators/table_2_value_table_with_header_comparator.py +27 -0
- holado_value/common/tables/converters/value_table_converter.py +217 -0
- holado_value/common/tables/value_table.py +29 -0
- holado_value/common/tables/value_table_cell.py +67 -0
- holado_value/common/tables/value_table_manager.py +58 -0
- holado_value/common/tables/value_table_row.py +44 -0
- holado_value/common/tables/value_table_with_header.py +28 -0
- holado_value/common/tools/unique_value_manager.py +108 -0
- holado_value/common/tools/value.py +164 -0
- holado_value/common/tools/value_types.py +35 -0
- holado_value/tests/behave/steps/__init__.py +16 -0
- holado_value/tests/behave/steps/private/__init__.py +16 -0
- holado_ws/__init__.py +16 -0
- holado_ws/api/ws/TODO.txt +2 -0
- holado_ws/tests/behave/steps/__init__.py +16 -0
- holado_ws/tests/behave/steps/api/web_service_steps.py +189 -0
- holado_yaml/__init__.py +31 -0
- holado_yaml/tests/behave/steps/__init__.py +16 -0
- holado_yaml/tests/behave/steps/yaml_steps.py +64 -0
- holado_yaml/yaml/yaml_manager.py +86 -0
- test_holado/Dockerfile_test_holado +82 -0
- test_holado/__init__.py +4 -0
- test_holado/__main__.py +25 -0
- test_holado/build_docker_image_to_test_holado_in_docker.sh +7 -0
- test_holado/environment.py +42 -0
- test_holado/features/NonReg/api/REST.feature +21 -0
- test_holado/features/NonReg/api/gRPC.feature +122 -0
- test_holado/features/NonReg/common/multiprocessing/simple.feature +60 -0
- test_holado/features/NonReg/common/system/commands.feature +43 -0
- test_holado/features/NonReg/common/system/system.feature +20 -0
- test_holado/features/NonReg/common/tables/table.feature +239 -0
- test_holado/features/NonReg/common/tables/value_table_conversion.feature +29 -0
- test_holado/features/NonReg/common/tools/DateTime.feature +88 -0
- test_holado/features/NonReg/common/tools/UniqueValueManager.feature +43 -0
- test_holado/features/NonReg/holado_ais/ais_message-bitarray_to_nmea.feature +135 -0
- test_holado/features/NonReg/holado_protobuf/protobuf.feature +291 -0
- test_holado/features/NonReg/holado_python/convert.feature +20 -0
- test_holado/features/NonReg/holado_python/iterable.feature +61 -0
- test_holado/features/NonReg/holado_python/standard_library/socket.feature +101 -0
- test_holado/features/NonReg/holado_python/standard_library/socket_with_ssl.feature +180 -0
- test_holado/features/NonReg/holado_scripting/common/tools/dynamic_text_manager.feature +18 -0
- test_holado/features/NonReg/holado_scripting/common/tools/expression_evaluator.feature +185 -0
- test_holado/features/NonReg/holado_scripting/common/tools/variable_manager.feature +71 -0
- test_holado/features/NonReg/holado_scripting/text/interpreter/interpreter.error.feature +21 -0
- test_holado/features/NonReg/holado_scripting/text/interpreter/interpreter.feature +120 -0
- test_holado/features/NonReg/holado_yaml/yaml.feature +218 -0
- test_holado/features/NonReg/ipc/bit_series.error.feature +33 -0
- test_holado/features/NonReg/ipc/bit_series.feature +131 -0
- test_holado/features/NonReg/ipc/json.feature +20 -0
- test_holado/features/NonReg/scenario/scenario.feature +139 -0
- test_holado/features/NonReg/test_steps/behave.feature +275 -0
- test_holado/features/NonReg/test_steps/common.feature +100 -0
- test_holado/features/NonReg/tools/RabbitMQ.feature +445 -0
- test_holado/features/NonReg/tools/RabbitMQ_steps.feature +276 -0
- test_holado/features/NonReg/tools/S3/boto3_client.feature +73 -0
- test_holado/features/NonReg/tools/S3/minio_client.feature +75 -0
- test_holado/features/NonReg/tools/db_sqlite3.feature +41 -0
- test_holado/features/NonReg/tools/sFTP.feature +25 -0
- test_holado/features/Test/logger.feature +28 -0
- test_holado/features/__init__.py +0 -0
- test_holado/logging.conf +41 -0
- test_holado/resources/proto/definitions/protobuf/custom_types/field_types.proto +24 -0
- test_holado/resources/proto/definitions/protobuf/protobuf.dev/tutorial/addressbook.proto +56 -0
- test_holado/resources/proto/generated/protobuf/custom_types/field_types_pb2.py +34 -0
- test_holado/resources/proto/generated/protobuf/protobuf/dev/tutorial/addressbook_pb2.py +34 -0
- test_holado/resources/scripts/list_tags.sh +2 -0
- test_holado/resources/scripts/update_resources_proto_generated.py +70 -0
- test_holado/steps/__init__.py +0 -0
- test_holado/steps/private_steps.py +20 -0
- test_holado/steps/public_steps.py +23 -0
- test_holado/test_holado_session_context.py +43 -0
- test_holado/tools/django/README.txt +3 -0
- test_holado/tools/django/api_grpc/api_grpc/__init__.py +0 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/__init__.py +0 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/admin.py +3 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/apps.py +6 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/migrations/__init__.py +0 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/models.py +3 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/proto/__init__.py +0 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/proto/account.proto +27 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/proto/account_pb2.py +33 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/proto/account_pb2_grpc.py +199 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/serializers.py +12 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/services.py +11 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/tests.py +3 -0
- test_holado/tools/django/api_grpc/api_grpc/api1/views.py +3 -0
- test_holado/tools/django/api_grpc/api_grpc/asgi.py +16 -0
- test_holado/tools/django/api_grpc/api_grpc/settings.py +126 -0
- test_holado/tools/django/api_grpc/api_grpc/urls.py +27 -0
- test_holado/tools/django/api_grpc/api_grpc/wsgi.py +16 -0
- test_holado/tools/django/api_grpc/db.sqlite3 +0 -0
- test_holado/tools/django/api_grpc/manage.py +27 -0
- test_holado/tools/django/api_grpc/manual_test_commands.txt +25 -0
- test_holado/tools/django/api_rest/api_rest/__init__.py +0 -0
- test_holado/tools/django/api_rest/api_rest/api1/__init__.py +0 -0
- test_holado/tools/django/api_rest/api_rest/api1/admin.py +3 -0
- test_holado/tools/django/api_rest/api_rest/api1/apps.py +6 -0
- test_holado/tools/django/api_rest/api_rest/api1/migrations/__init__.py +0 -0
- test_holado/tools/django/api_rest/api_rest/api1/models.py +3 -0
- test_holado/tools/django/api_rest/api_rest/api1/serializers.py +15 -0
- test_holado/tools/django/api_rest/api_rest/api1/tests.py +3 -0
- test_holado/tools/django/api_rest/api_rest/api1/views.py +24 -0
- test_holado/tools/django/api_rest/api_rest/asgi.py +16 -0
- test_holado/tools/django/api_rest/api_rest/settings.py +133 -0
- test_holado/tools/django/api_rest/api_rest/urls.py +32 -0
- test_holado/tools/django/api_rest/api_rest/wsgi.py +16 -0
- test_holado/tools/django/api_rest/db.sqlite3 +0 -0
- test_holado/tools/django/api_rest/manage.py +22 -0
- holado-0.2.2.dist-info/RECORD +0 -17
- {holado-0.2.2.dist-info → holado-0.2.4.dist-info}/WHEEL +0 -0
- {holado-0.2.2.dist-info → holado-0.2.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1303 @@
|
|
|
1
|
+
|
|
2
|
+
#################################################
|
|
3
|
+
# HolAdo (Holistic Automation do)
|
|
4
|
+
#
|
|
5
|
+
# (C) Copyright 2021-2025 by Eric Klumpp
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
+
#
|
|
9
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
+
|
|
11
|
+
# The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software.
|
|
12
|
+
#################################################
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#################################################
|
|
16
|
+
#
|
|
17
|
+
# Patches are done to follow ITU recommendation:
|
|
18
|
+
# https://www.e-navigation.nl/sites/default/files/R-REC-M.1371-5-201402-I!!PDF-E_1.pdf
|
|
19
|
+
#
|
|
20
|
+
#################################################
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from bitarray import bitarray
|
|
25
|
+
import attr
|
|
26
|
+
import typing
|
|
27
|
+
import pyais
|
|
28
|
+
from pyais.messages import Payload, bit_field, from_mmsi, NMEA_VALUE, CommunicationStateMixin, TagBlock, ASTERISK, from_turn, to_turn, from_speed, to_speed,\
|
|
29
|
+
from_lat_lon, to_lat_lon, from_10th, to_10th, from_lat_lon_600, to_lat_lon_600
|
|
30
|
+
from pyais.util import get_int, from_bytes_signed, from_bytes, bits2bytes, checksum, chunks, compute_checksum, str_to_bin, bytes2bits
|
|
31
|
+
from pyais.exceptions import InvalidDataTypeException
|
|
32
|
+
from pyais.encode import AIS_SENTENCES, DATA_DICT, get_ais_type, data_to_payload
|
|
33
|
+
import math
|
|
34
|
+
from pyais.constants import NavigationStatus, TurnRate, ManeuverIndicator, EpfdType, ShipType, NavAid, StationType, TransmitMode, StationIntervals
|
|
35
|
+
import functools
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Add possibility to specify the seq_id in AIS sentence
|
|
42
|
+
# Note: parameter seq_id is just added in some methods of pyais.encode module
|
|
43
|
+
|
|
44
|
+
def HA_ais_to_nmea_0183(payload: str, ais_talker_id: str, radio_channel: str, fill_bits: int, seq_id: str = None) -> AIS_SENTENCES:
|
|
45
|
+
"""
|
|
46
|
+
Splits the AIS payload into sentences, ASCII encodes the payload, creates
|
|
47
|
+
and sends the relevant NMEA 0183 sentences. Messages have a maximum length
|
|
48
|
+
of 82 characters, including the $ or ! starting character and the ending <LF>.
|
|
49
|
+
|
|
50
|
+
HINT:
|
|
51
|
+
This method takes care of splitting large payloads (larger than 60 characters)
|
|
52
|
+
into multiple sentences. With a total of 80 maximum chars excluding end of line
|
|
53
|
+
per sentence, and 20 chars head + tail in the nmea 0183 carrier protocol, 60
|
|
54
|
+
chars remain for the actual payload.
|
|
55
|
+
|
|
56
|
+
@param payload: Armored AIs payload.
|
|
57
|
+
@param ais_talker_id: AIS talker ID (AIVDO or AIVDM)
|
|
58
|
+
@param radio_channel: Radio channel (either A or B)
|
|
59
|
+
@param fill_bits: The number of fill bits requires to pad the data payload to a 6 bit boundary.
|
|
60
|
+
@return: A list of relevant AIS sentences.
|
|
61
|
+
"""
|
|
62
|
+
messages = []
|
|
63
|
+
max_len = 60
|
|
64
|
+
frag_cnt = math.ceil(len(payload) / max_len)
|
|
65
|
+
if seq_id is None:
|
|
66
|
+
seq_id = '0' if frag_cnt > 1 else ''
|
|
67
|
+
elif frag_cnt == 1:
|
|
68
|
+
seq_id = ''
|
|
69
|
+
|
|
70
|
+
if len(ais_talker_id) != 5:
|
|
71
|
+
raise ValueError("AIS talker is must have exactly 6 characters. E.g. AIVDO")
|
|
72
|
+
|
|
73
|
+
if len(radio_channel) != 1:
|
|
74
|
+
raise ValueError("Radio channel must be a single character")
|
|
75
|
+
|
|
76
|
+
for frag_num, chunk in enumerate(chunks(payload, max_len), start=1):
|
|
77
|
+
tpl = "!{},{},{},{},{},{},{}*{:02X}"
|
|
78
|
+
fill_bits_frag = fill_bits if frag_num == frag_cnt else 0 # Make sure we set fill bits only for last fragment
|
|
79
|
+
dummy_message = tpl.format(ais_talker_id, frag_cnt, frag_num, seq_id, radio_channel, chunk, fill_bits_frag, 0)
|
|
80
|
+
checksum = compute_checksum(dummy_message)
|
|
81
|
+
msg = tpl.format(ais_talker_id, frag_cnt, frag_num, seq_id, radio_channel, chunk, fill_bits_frag, checksum)
|
|
82
|
+
messages.append(msg)
|
|
83
|
+
|
|
84
|
+
return messages
|
|
85
|
+
|
|
86
|
+
pyais.ais_to_nmea_0183 = HA_ais_to_nmea_0183
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def HA_encode_dict(data: DATA_DICT, talker_id: str = "AIVDO", radio_channel: str = "A", seq_id: str = None) -> AIS_SENTENCES:
|
|
90
|
+
"""
|
|
91
|
+
Takes a dictionary of data and some NMEA specific kwargs and returns the NMEA 0183 encoded AIS sentence.
|
|
92
|
+
|
|
93
|
+
Notes:
|
|
94
|
+
- the data dict should also contain the AIS message type (1-27) under the `type` key.
|
|
95
|
+
- different messages take different keywords. Refer to the payload classes above to get a glimpse
|
|
96
|
+
on what fields each AIS message can take.
|
|
97
|
+
|
|
98
|
+
@param data: The AIS data as a dictionary.
|
|
99
|
+
@param talker_id: AIS packets have the introducer "AIVDM" or "AIVDO";
|
|
100
|
+
AIVDM packets are reports from other ships and AIVDO packets are reports from your own ship.
|
|
101
|
+
@param radio_channel: The radio channel. Can be either 'A' (default) or 'B'.
|
|
102
|
+
@return: NMEA 0183 encoded AIS sentences.
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
if talker_id not in ("AIVDM", "AIVDO"):
|
|
106
|
+
raise ValueError("talker_id must be any of ['AIVDM', 'AIVDO']")
|
|
107
|
+
|
|
108
|
+
if radio_channel not in ('A', 'B'):
|
|
109
|
+
raise ValueError("radio_channel must be any of ['A', 'B']")
|
|
110
|
+
|
|
111
|
+
ais_type = get_ais_type(data)
|
|
112
|
+
payload = data_to_payload(ais_type, data)
|
|
113
|
+
armored_payload, fill_bits = payload.encode()
|
|
114
|
+
return pyais.ais_to_nmea_0183(armored_payload, talker_id, radio_channel, fill_bits, seq_id=seq_id)
|
|
115
|
+
|
|
116
|
+
pyais.encode_dict = HA_encode_dict
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def HA_encode_msg(msg: Payload, talker_id: str = "AIVDO", radio_channel: str = "A", seq_id: str = None) -> AIS_SENTENCES:
|
|
120
|
+
if talker_id not in ("AIVDM", "AIVDO"):
|
|
121
|
+
raise ValueError("talker_id must be any of ['AIVDM', 'AIVDO']")
|
|
122
|
+
|
|
123
|
+
if radio_channel not in ('A', 'B'):
|
|
124
|
+
raise ValueError("radio_channel must be any of ['A', 'B']")
|
|
125
|
+
|
|
126
|
+
armored_payload, fill_bits = msg.encode()
|
|
127
|
+
return pyais.ais_to_nmea_0183(armored_payload, talker_id, radio_channel, fill_bits, seq_id=seq_id)
|
|
128
|
+
|
|
129
|
+
pyais.encode_msg = HA_encode_msg
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# All @ at end of string are considered padding.
|
|
133
|
+
# Note: in pyais implementation, decode is stopped at first @.
|
|
134
|
+
def HA_decode_bin_as_ascii6(bit_arr: bitarray) -> str:
|
|
135
|
+
"""
|
|
136
|
+
Decode binary data as 6 bit ASCII.
|
|
137
|
+
:param bit_arr: array of bits
|
|
138
|
+
:return: ASCII String
|
|
139
|
+
"""
|
|
140
|
+
string: str = ""
|
|
141
|
+
c: bitarray
|
|
142
|
+
for c in chunks(bit_arr, 6): # type:ignore
|
|
143
|
+
n: int = from_bytes(c.tobytes()) >> 2
|
|
144
|
+
|
|
145
|
+
# Last entry may not have 6 bits
|
|
146
|
+
if len(c) != 6:
|
|
147
|
+
n >> (6 - len(c))
|
|
148
|
+
|
|
149
|
+
if n < 0x20:
|
|
150
|
+
n += 0x40
|
|
151
|
+
|
|
152
|
+
string += chr(n)
|
|
153
|
+
|
|
154
|
+
# Remove all @ at end since they correspond to padding
|
|
155
|
+
string = string.rstrip('@')
|
|
156
|
+
|
|
157
|
+
# Remove spaces as some encoders make padding with spaces instead of @
|
|
158
|
+
return string.strip()
|
|
159
|
+
|
|
160
|
+
pyais.util.decode_bin_as_ascii6 = HA_decode_bin_as_ascii6
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def HA_int_to_bin(val: typing.Union[int, bool], width: int, signed: bool = True) -> bitarray:
|
|
164
|
+
"""
|
|
165
|
+
Convert an integer or boolean value to binary.
|
|
166
|
+
Compared to pyais implementation, the behaviour is changed if the value is too great to fit into
|
|
167
|
+
`width` bits:
|
|
168
|
+
- in pyais, the maximum possible number that still fits is used,
|
|
169
|
+
- in this implementation, an exception is raised.
|
|
170
|
+
|
|
171
|
+
@param val: Any integer or boolean value.
|
|
172
|
+
@param width: The bit width. If less than width bits are required, leading zeros are added.
|
|
173
|
+
@param signed: Set to True/False if the value is signed or not.
|
|
174
|
+
@return: The binary representation of value with exactly width bits. Type is bitarray.
|
|
175
|
+
"""
|
|
176
|
+
# Compute the total number of bytes required to hold up to `width` bits.
|
|
177
|
+
n_bytes, mod = divmod(width, 8)
|
|
178
|
+
if mod > 0:
|
|
179
|
+
n_bytes += 1
|
|
180
|
+
|
|
181
|
+
# If the value is too big, return a bitarray of all 1's
|
|
182
|
+
mask = (1 << width) - 1
|
|
183
|
+
if val > mask:
|
|
184
|
+
raise ValueError(f"Value {val} is too big for bit width {width} (max possible value: {mask})")
|
|
185
|
+
|
|
186
|
+
bits = bitarray(endian='big')
|
|
187
|
+
bits.frombytes(val.to_bytes(n_bytes, 'big', signed=signed))
|
|
188
|
+
return bits[8 - mod if mod else 0:]
|
|
189
|
+
|
|
190
|
+
pyais.util.int_to_bin = HA_int_to_bin
|
|
191
|
+
pyais.messages.int_to_bin = HA_int_to_bin
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# Add spare field management
|
|
195
|
+
|
|
196
|
+
def bit_field(width: int, d_type: typing.Type[typing.Any],
|
|
197
|
+
from_converter: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
|
|
198
|
+
to_converter: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
|
|
199
|
+
default: typing.Optional[typing.Any] = None,
|
|
200
|
+
signed: bool = False,
|
|
201
|
+
variable_length: bool = False,
|
|
202
|
+
is_spare: bool = False,
|
|
203
|
+
**kwargs: typing.Any) -> typing.Any:
|
|
204
|
+
"""Same as pyais bit_field function, but adds a metadata 'is_spare' at True.
|
|
205
|
+
Metadata 'is_spare' is used to know which fields must not be exported when converting message to dict or json.
|
|
206
|
+
"""
|
|
207
|
+
return attr.ib(
|
|
208
|
+
metadata={
|
|
209
|
+
'width': width,
|
|
210
|
+
'd_type': d_type,
|
|
211
|
+
'from_converter': from_converter,
|
|
212
|
+
'to_converter': to_converter,
|
|
213
|
+
'signed': signed,
|
|
214
|
+
'default': default,
|
|
215
|
+
'variable_length': variable_length,
|
|
216
|
+
'is_spare': is_spare
|
|
217
|
+
},
|
|
218
|
+
**kwargs
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
pyais.messages.bit_field = bit_field
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@attr.s(slots=True)
|
|
225
|
+
class HAPayload(Payload):
|
|
226
|
+
"""Payload class with spare fields management.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def asdict(self, enum_as_int: bool = False, with_spare: bool = False) -> typing.Dict[str, typing.Optional[NMEA_VALUE]]:
|
|
230
|
+
res = super().asdict(enum_as_int)
|
|
231
|
+
|
|
232
|
+
# Remove spare fields
|
|
233
|
+
if not with_spare:
|
|
234
|
+
for field in self.fields():
|
|
235
|
+
if 'is_spare' in field.metadata and field.metadata['is_spare']:
|
|
236
|
+
del res[field.name]
|
|
237
|
+
|
|
238
|
+
return res
|
|
239
|
+
|
|
240
|
+
@classmethod
|
|
241
|
+
def from_bitarray(cls, bit_arr: bitarray) -> "ANY_MESSAGE":
|
|
242
|
+
cur: int = 0
|
|
243
|
+
end: int = 0
|
|
244
|
+
length: int = len(bit_arr)
|
|
245
|
+
kwargs: typing.Dict[str, typing.Any] = {}
|
|
246
|
+
|
|
247
|
+
# Manage field with a variable length
|
|
248
|
+
index_variable_length_field: int = None
|
|
249
|
+
cursor_variable_length_field_beg: int = None
|
|
250
|
+
cursor_variable_length_field_end: int = None
|
|
251
|
+
|
|
252
|
+
# Iterate over the bits until a variable length field or the last bit of the bitarray or all fields are fully decoded
|
|
253
|
+
for field_index, field in enumerate(cls.fields()):
|
|
254
|
+
# Stop if field has a variable length
|
|
255
|
+
if field.metadata['variable_length']:
|
|
256
|
+
index_variable_length_field = field_index
|
|
257
|
+
cursor_variable_length_field_beg = cur
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
if end >= length:
|
|
261
|
+
# All fields that did not fit into the bit array are None
|
|
262
|
+
kwargs[field.name] = None
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
width = field.metadata['width']
|
|
266
|
+
end = min(length, cur + width)
|
|
267
|
+
bits = bit_arr[cur: end]
|
|
268
|
+
|
|
269
|
+
kwargs[field.name] = cls._bitarray_to_field_type(bits, field)
|
|
270
|
+
cur = end
|
|
271
|
+
|
|
272
|
+
if index_variable_length_field is not None:
|
|
273
|
+
# Iterate over fields in reverse order until variable length field
|
|
274
|
+
end = length
|
|
275
|
+
cur = length
|
|
276
|
+
|
|
277
|
+
last_fields = tuple(cls.fields())[index_variable_length_field+1:]
|
|
278
|
+
for field in reversed(last_fields):
|
|
279
|
+
if end <= cursor_variable_length_field_beg:
|
|
280
|
+
# All fields that did not fit into the bit array are None
|
|
281
|
+
kwargs[field.name] = None
|
|
282
|
+
continue
|
|
283
|
+
|
|
284
|
+
width = field.metadata['width']
|
|
285
|
+
cur = max(cursor_variable_length_field_beg, end - width)
|
|
286
|
+
bits = bit_arr[cur: end]
|
|
287
|
+
|
|
288
|
+
kwargs[field.name] = cls._bitarray_to_field_type(bits, field)
|
|
289
|
+
end = cur
|
|
290
|
+
|
|
291
|
+
# Add variable length field
|
|
292
|
+
cursor_variable_length_field_end = end
|
|
293
|
+
field = cls.fields()[index_variable_length_field]
|
|
294
|
+
bits = bit_arr[cursor_variable_length_field_beg: cursor_variable_length_field_end]
|
|
295
|
+
kwargs[field.name] = cls._bitarray_to_field_type(bits, field)
|
|
296
|
+
|
|
297
|
+
return cls(**kwargs) # type:ignore
|
|
298
|
+
|
|
299
|
+
@classmethod
|
|
300
|
+
def _bitarray_to_field_type(cls, bits, field) -> typing.Any:
|
|
301
|
+
val: typing.Any
|
|
302
|
+
d_type = field.metadata['d_type']
|
|
303
|
+
converter = field.metadata['to_converter']
|
|
304
|
+
|
|
305
|
+
# Get the correct data type and decoding function
|
|
306
|
+
if d_type == int or d_type == bool or d_type == float:
|
|
307
|
+
shift = (8 - (len(bits) % 8)) % 8
|
|
308
|
+
if field.metadata['signed']:
|
|
309
|
+
val = from_bytes_signed(bits) >> shift
|
|
310
|
+
else:
|
|
311
|
+
val = from_bytes(bits) >> shift
|
|
312
|
+
|
|
313
|
+
if d_type == float:
|
|
314
|
+
val = float(val)
|
|
315
|
+
elif d_type == bool:
|
|
316
|
+
val = bool(val)
|
|
317
|
+
|
|
318
|
+
elif d_type == str:
|
|
319
|
+
val = HA_decode_bin_as_ascii6(bits)
|
|
320
|
+
elif d_type == bytes:
|
|
321
|
+
val = bits2bytes(bits)
|
|
322
|
+
else:
|
|
323
|
+
raise InvalidDataTypeException(d_type)
|
|
324
|
+
|
|
325
|
+
val = converter(val) if converter is not None else val
|
|
326
|
+
|
|
327
|
+
return val
|
|
328
|
+
|
|
329
|
+
def to_bitarray(self) -> bitarray:
|
|
330
|
+
"""
|
|
331
|
+
Convert all attributes of a given Payload/Message to binary.
|
|
332
|
+
"""
|
|
333
|
+
out = bitarray()
|
|
334
|
+
for field in self.fields():
|
|
335
|
+
width = field.metadata['width']
|
|
336
|
+
d_type = field.metadata['d_type']
|
|
337
|
+
converter = field.metadata['from_converter']
|
|
338
|
+
signed = field.metadata['signed']
|
|
339
|
+
variable_length = field.metadata['variable_length']
|
|
340
|
+
|
|
341
|
+
val = getattr(self, field.name)
|
|
342
|
+
if val is None:
|
|
343
|
+
continue
|
|
344
|
+
|
|
345
|
+
val = converter(val) if converter is not None else val
|
|
346
|
+
|
|
347
|
+
try:
|
|
348
|
+
if d_type in (bool, int):
|
|
349
|
+
bits = HA_int_to_bin(val, width, signed=signed)
|
|
350
|
+
elif d_type == float:
|
|
351
|
+
val = int(val)
|
|
352
|
+
bits = HA_int_to_bin(val, width, signed=signed)
|
|
353
|
+
elif d_type == str:
|
|
354
|
+
trailing_spaces = not variable_length
|
|
355
|
+
bits = str_to_bin(val, width, trailing_spaces=trailing_spaces)
|
|
356
|
+
elif d_type == bytes:
|
|
357
|
+
bits = bytes2bits(val, default=bitarray('0' * width))
|
|
358
|
+
else:
|
|
359
|
+
raise InvalidDataTypeException(d_type)
|
|
360
|
+
except ValueError as exc:
|
|
361
|
+
raise ValueError(f"Invalid value for field '{field.name}': {exc}") from None
|
|
362
|
+
|
|
363
|
+
bits = bits[:width]
|
|
364
|
+
out += bits
|
|
365
|
+
|
|
366
|
+
return out
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# Fix message types:
|
|
370
|
+
# - almost all types with new spare management
|
|
371
|
+
# - type 21: exclude full_name property from asdict result
|
|
372
|
+
# - type 26: fix create and from_bitarray methods
|
|
373
|
+
|
|
374
|
+
@attr.s(slots=True)
|
|
375
|
+
class HAMessageType1(HAPayload, CommunicationStateMixin):
|
|
376
|
+
"""
|
|
377
|
+
AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
|
|
378
|
+
"""
|
|
379
|
+
msg_type = bit_field(6, int, default=1, signed=False)
|
|
380
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
381
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
382
|
+
status = bit_field(4, int, default=0, converter=NavigationStatus.from_value, signed=False)
|
|
383
|
+
turn = bit_field(8, float, default=TurnRate.NO_TI_DEFAULT, signed=True, to_converter=to_turn, from_converter=from_turn)
|
|
384
|
+
speed = bit_field(10, float, from_converter=from_speed, to_converter=to_speed, default=0, signed=False)
|
|
385
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
386
|
+
lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, default=0, signed=True)
|
|
387
|
+
lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, default=0, signed=True)
|
|
388
|
+
course = bit_field(12, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)
|
|
389
|
+
heading = bit_field(9, int, default=0, signed=False)
|
|
390
|
+
second = bit_field(6, int, default=0, signed=False)
|
|
391
|
+
maneuver = bit_field(2, int, default=0, from_converter=ManeuverIndicator.from_value,
|
|
392
|
+
to_converter=ManeuverIndicator.from_value, signed=False)
|
|
393
|
+
spare_1 = bit_field(3, bytes, default=b'', is_spare=True)
|
|
394
|
+
raim = bit_field(1, bool, default=0)
|
|
395
|
+
radio = bit_field(19, int, default=0, signed=False)
|
|
396
|
+
|
|
397
|
+
pyais.messages.MessageType1 = HAMessageType1
|
|
398
|
+
pyais.messages.MSG_CLASS[1] = pyais.messages.MessageType1
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class HAMessageType2(HAMessageType1):
|
|
402
|
+
"""
|
|
403
|
+
AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
|
|
404
|
+
"""
|
|
405
|
+
msg_type = bit_field(6, int, default=2)
|
|
406
|
+
|
|
407
|
+
pyais.messages.MessageType2 = HAMessageType2
|
|
408
|
+
pyais.messages.MSG_CLASS[2] = pyais.messages.MessageType2
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
class HAMessageType3(HAMessageType1):
|
|
412
|
+
"""
|
|
413
|
+
AIS Vessel position report using ITDMA (Incremental Time Division Multiple Access)
|
|
414
|
+
"""
|
|
415
|
+
msg_type = bit_field(6, int, default=3)
|
|
416
|
+
|
|
417
|
+
pyais.messages.MessageType3 = HAMessageType3
|
|
418
|
+
pyais.messages.MSG_CLASS[3] = pyais.messages.MessageType3
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
@attr.s(slots=True)
|
|
422
|
+
class HAMessageType4(HAPayload, CommunicationStateMixin):
|
|
423
|
+
"""
|
|
424
|
+
AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
|
|
425
|
+
"""
|
|
426
|
+
msg_type = bit_field(6, int, default=4, signed=False)
|
|
427
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
428
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
429
|
+
year = bit_field(14, int, default=1970, signed=False)
|
|
430
|
+
month = bit_field(4, int, default=1, signed=False)
|
|
431
|
+
day = bit_field(5, int, default=1, signed=False)
|
|
432
|
+
hour = bit_field(5, int, default=0, signed=False)
|
|
433
|
+
minute = bit_field(6, int, default=0, signed=False)
|
|
434
|
+
second = bit_field(6, int, default=0, signed=False)
|
|
435
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
436
|
+
lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
437
|
+
lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
438
|
+
epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value,
|
|
439
|
+
signed=False)
|
|
440
|
+
spare_1 = bit_field(10, bytes, default=b'', is_spare=True)
|
|
441
|
+
raim = bit_field(1, bool, default=0)
|
|
442
|
+
radio = bit_field(19, int, default=0, signed=False)
|
|
443
|
+
|
|
444
|
+
pyais.messages.MessageType4 = HAMessageType4
|
|
445
|
+
pyais.messages.MSG_CLASS[4] = pyais.messages.MessageType4
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
@attr.s(slots=True)
|
|
449
|
+
class HAMessageType5(HAPayload):
|
|
450
|
+
"""
|
|
451
|
+
Static and Voyage Related Data
|
|
452
|
+
"""
|
|
453
|
+
msg_type = bit_field(6, int, default=5, signed=False)
|
|
454
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
455
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
456
|
+
ais_version = bit_field(2, int, default=0, signed=False)
|
|
457
|
+
imo = bit_field(30, int, default=0, signed=False)
|
|
458
|
+
callsign = bit_field(42, str, default='')
|
|
459
|
+
shipname = bit_field(120, str, default='')
|
|
460
|
+
ship_type = bit_field(8, int, default=0, from_converter=ShipType.from_value, to_converter=ShipType.from_value)
|
|
461
|
+
to_bow = bit_field(9, int, default=0, signed=False)
|
|
462
|
+
to_stern = bit_field(9, int, default=0, signed=False)
|
|
463
|
+
to_port = bit_field(6, int, default=0, signed=False)
|
|
464
|
+
to_starboard = bit_field(6, int, default=0, signed=False)
|
|
465
|
+
epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
|
|
466
|
+
month = bit_field(4, int, default=0, signed=False)
|
|
467
|
+
day = bit_field(5, int, default=0, signed=False)
|
|
468
|
+
hour = bit_field(5, int, default=0, signed=False)
|
|
469
|
+
minute = bit_field(6, int, default=0, signed=False)
|
|
470
|
+
draught = bit_field(8, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)
|
|
471
|
+
destination = bit_field(120, str, default='')
|
|
472
|
+
dte = bit_field(1, bool, default=0, signed=False)
|
|
473
|
+
spare_1 = bit_field(1, bytes, default=b'', is_spare=True)
|
|
474
|
+
|
|
475
|
+
pyais.messages.MessageType5 = HAMessageType5
|
|
476
|
+
pyais.messages.MSG_CLASS[5] = pyais.messages.MessageType5
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
@attr.s(slots=True)
|
|
480
|
+
class HAMessageType6(HAPayload):
|
|
481
|
+
"""
|
|
482
|
+
Binary Addresses Message
|
|
483
|
+
"""
|
|
484
|
+
msg_type = bit_field(6, int, default=6)
|
|
485
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
486
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
487
|
+
seqno = bit_field(2, int, default=0, signed=False)
|
|
488
|
+
dest_mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
489
|
+
retransmit = bit_field(1, bool, default=False, signed=False)
|
|
490
|
+
spare_1 = bit_field(1, bytes, default=b'', is_spare=True)
|
|
491
|
+
dac = bit_field(10, int, default=0, signed=False)
|
|
492
|
+
fid = bit_field(6, int, default=0, signed=False)
|
|
493
|
+
data = bit_field(920, bytes, default=b'', variable_length=True)
|
|
494
|
+
|
|
495
|
+
pyais.messages.MessageType6 = HAMessageType6
|
|
496
|
+
pyais.messages.MSG_CLASS[6] = pyais.messages.MessageType6
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@attr.s(slots=True)
|
|
500
|
+
class HAMessageType7(HAPayload):
|
|
501
|
+
"""
|
|
502
|
+
Binary Acknowledge
|
|
503
|
+
"""
|
|
504
|
+
msg_type = bit_field(6, int, default=7, signed=False)
|
|
505
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
506
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
507
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
508
|
+
mmsi1 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
509
|
+
mmsiseq1 = bit_field(2, int, default=0, signed=False)
|
|
510
|
+
mmsi2 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
511
|
+
mmsiseq2 = bit_field(2, int, default=0, signed=False)
|
|
512
|
+
mmsi3 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
513
|
+
mmsiseq3 = bit_field(2, int, default=0, signed=False)
|
|
514
|
+
mmsi4 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
515
|
+
mmsiseq4 = bit_field(2, int, default=0, signed=False)
|
|
516
|
+
|
|
517
|
+
pyais.messages.MessageType7 = HAMessageType7
|
|
518
|
+
pyais.messages.MSG_CLASS[7] = pyais.messages.MessageType7
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
@attr.s(slots=True)
|
|
522
|
+
class HAMessageType8(HAPayload):
|
|
523
|
+
"""
|
|
524
|
+
Binary Acknowledge
|
|
525
|
+
"""
|
|
526
|
+
msg_type = bit_field(6, int, default=8, signed=False)
|
|
527
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
528
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
529
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
530
|
+
dac = bit_field(10, int, default=0, signed=False)
|
|
531
|
+
fid = bit_field(6, int, default=0, signed=False)
|
|
532
|
+
data = bit_field(952, bytes, default=b'', variable_length=True)
|
|
533
|
+
|
|
534
|
+
pyais.messages.MessageType8 = HAMessageType8
|
|
535
|
+
pyais.messages.MSG_CLASS[8] = pyais.messages.MessageType8
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@attr.s(slots=True)
|
|
539
|
+
class HAMessageType9(HAPayload, CommunicationStateMixin):
|
|
540
|
+
"""
|
|
541
|
+
Standard SAR Aircraft Position Report
|
|
542
|
+
"""
|
|
543
|
+
msg_type = bit_field(6, int, default=9, signed=False)
|
|
544
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
545
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
546
|
+
|
|
547
|
+
alt = bit_field(12, int, default=0, signed=False)
|
|
548
|
+
# speed over ground is in knots, not deciknots
|
|
549
|
+
speed = bit_field(10, float, default=0, signed=False)
|
|
550
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
551
|
+
lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
552
|
+
lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
553
|
+
course = bit_field(12, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)
|
|
554
|
+
second = bit_field(6, int, default=0, signed=False)
|
|
555
|
+
|
|
556
|
+
reserved_1 = bit_field(8, int, default=0)
|
|
557
|
+
dte = bit_field(1, bool, default=0)
|
|
558
|
+
spare_1 = bit_field(3, bytes, default=b'', is_spare=True)
|
|
559
|
+
assigned = bit_field(1, bool, default=0)
|
|
560
|
+
raim = bit_field(1, bool, default=0)
|
|
561
|
+
radio = bit_field(20, int, default=0, signed=False)
|
|
562
|
+
|
|
563
|
+
pyais.messages.MessageType9 = HAMessageType9
|
|
564
|
+
pyais.messages.MSG_CLASS[9] = pyais.messages.MessageType9
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
@attr.s(slots=True)
|
|
568
|
+
class HAMessageType10(HAPayload):
|
|
569
|
+
"""
|
|
570
|
+
UTC/Date Inquiry
|
|
571
|
+
"""
|
|
572
|
+
msg_type = bit_field(6, int, default=10, signed=False)
|
|
573
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
574
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
575
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
576
|
+
dest_mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
577
|
+
spare_2 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
578
|
+
|
|
579
|
+
pyais.messages.MessageType10 = HAMessageType10
|
|
580
|
+
pyais.messages.MSG_CLASS[10] = pyais.messages.MessageType10
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
class HAMessageType11(HAMessageType4):
|
|
584
|
+
"""
|
|
585
|
+
UTC/Date Response
|
|
586
|
+
"""
|
|
587
|
+
msg_type = bit_field(6, int, default=11, signed=False)
|
|
588
|
+
|
|
589
|
+
pyais.messages.MessageType11 = HAMessageType11
|
|
590
|
+
pyais.messages.MSG_CLASS[11] = pyais.messages.MessageType11
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
@attr.s(slots=True)
|
|
594
|
+
class HAMessageType12(HAPayload):
|
|
595
|
+
"""
|
|
596
|
+
Addressed Safety-Related Message
|
|
597
|
+
"""
|
|
598
|
+
msg_type = bit_field(6, int, default=12, signed=False)
|
|
599
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
600
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
601
|
+
seqno = bit_field(2, int, default=0, signed=False)
|
|
602
|
+
dest_mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
603
|
+
retransmit = bit_field(1, bool, default=False, signed=False)
|
|
604
|
+
spare_1 = bit_field(1, bytes, default=b'', is_spare=True)
|
|
605
|
+
text = bit_field(936, str, default='', variable_length=True)
|
|
606
|
+
|
|
607
|
+
pyais.messages.MessageType12 = HAMessageType12
|
|
608
|
+
pyais.messages.MSG_CLASS[12] = pyais.messages.MessageType12
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
class HAMessageType13(HAMessageType7):
|
|
612
|
+
"""
|
|
613
|
+
Identical to type 7
|
|
614
|
+
"""
|
|
615
|
+
msg_type = bit_field(6, int, default=13, signed=False)
|
|
616
|
+
|
|
617
|
+
pyais.messages.MessageType13 = HAMessageType13
|
|
618
|
+
pyais.messages.MSG_CLASS[13] = pyais.messages.MessageType13
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
@attr.s(slots=True)
|
|
622
|
+
class HAMessageType14(HAPayload):
|
|
623
|
+
"""
|
|
624
|
+
Safety-Related Broadcast Message
|
|
625
|
+
"""
|
|
626
|
+
msg_type = bit_field(6, int, default=14, signed=False)
|
|
627
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
628
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
629
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
630
|
+
text = bit_field(968, str, default='', variable_length=True)
|
|
631
|
+
|
|
632
|
+
pyais.messages.MessageType14 = HAMessageType14
|
|
633
|
+
pyais.messages.MSG_CLASS[14] = pyais.messages.MessageType14
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
@attr.s(slots=True)
|
|
637
|
+
class HAMessageType15(HAPayload):
|
|
638
|
+
"""
|
|
639
|
+
Interrogation
|
|
640
|
+
"""
|
|
641
|
+
msg_type = bit_field(6, int, default=15, signed=False)
|
|
642
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
643
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
644
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
645
|
+
mmsi1 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
646
|
+
type1_1 = bit_field(6, int, default=0, signed=False)
|
|
647
|
+
offset1_1 = bit_field(12, int, default=0, signed=False)
|
|
648
|
+
spare_2 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
649
|
+
type1_2 = bit_field(6, int, default=0, signed=False)
|
|
650
|
+
offset1_2 = bit_field(12, int, default=0, signed=False)
|
|
651
|
+
spare_3 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
652
|
+
mmsi2 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
653
|
+
type2_1 = bit_field(6, int, default=0, signed=False)
|
|
654
|
+
offset2_1 = bit_field(12, int, default=0, signed=False)
|
|
655
|
+
spare_4 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
656
|
+
|
|
657
|
+
pyais.messages.MessageType15 = HAMessageType15
|
|
658
|
+
pyais.messages.MSG_CLASS[15] = pyais.messages.MessageType15
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
@attr.s(slots=True)
|
|
662
|
+
class HAMessageType16DestinationA(HAPayload):
|
|
663
|
+
"""
|
|
664
|
+
Assignment Mode Command
|
|
665
|
+
"""
|
|
666
|
+
msg_type = bit_field(6, int, default=16, signed=False)
|
|
667
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
668
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
669
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
670
|
+
|
|
671
|
+
mmsi1 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
672
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
673
|
+
increment1 = bit_field(10, int, default=0, signed=False)
|
|
674
|
+
spare_2 = bit_field(4, bytes, default=b'', is_spare=True)
|
|
675
|
+
|
|
676
|
+
pyais.messages.MessageType16DestinationA = HAMessageType16DestinationA
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
@attr.s(slots=True)
|
|
680
|
+
class HAMessageType16DestinationAB(HAPayload):
|
|
681
|
+
"""
|
|
682
|
+
Assignment Mode Command
|
|
683
|
+
"""
|
|
684
|
+
msg_type = bit_field(6, int, default=16, signed=False)
|
|
685
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
686
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
687
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
688
|
+
|
|
689
|
+
mmsi1 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
690
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
691
|
+
increment1 = bit_field(10, int, default=0, signed=False)
|
|
692
|
+
|
|
693
|
+
mmsi2 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
694
|
+
offset2 = bit_field(12, int, default=0, signed=False)
|
|
695
|
+
increment2 = bit_field(10, int, default=0, signed=False)
|
|
696
|
+
|
|
697
|
+
pyais.messages.MessageType16DestinationAB = HAMessageType16DestinationAB
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
@attr.s(slots=True)
|
|
701
|
+
class HAMessageType16(HAPayload):
|
|
702
|
+
@classmethod
|
|
703
|
+
def create(cls, **kwargs: typing.Union[str, float, int, bool, bytes]) -> "ANY_MESSAGE":
|
|
704
|
+
if 'mmsi2' in kwargs:
|
|
705
|
+
return HAMessageType16DestinationAB.create(**kwargs)
|
|
706
|
+
else:
|
|
707
|
+
return HAMessageType16DestinationA.create(**kwargs)
|
|
708
|
+
|
|
709
|
+
@classmethod
|
|
710
|
+
def from_bitarray(cls, bit_arr: bitarray) -> "ANY_MESSAGE":
|
|
711
|
+
if len(bit_arr) > 96:
|
|
712
|
+
return HAMessageType16DestinationAB.from_bitarray(bit_arr)
|
|
713
|
+
else:
|
|
714
|
+
return HAMessageType16DestinationA.from_bitarray(bit_arr)
|
|
715
|
+
|
|
716
|
+
pyais.messages.MessageType16 = HAMessageType16
|
|
717
|
+
pyais.messages.MSG_CLASS[16] = pyais.messages.MessageType16
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
@attr.s(slots=True)
|
|
721
|
+
class HAMessageType17(HAPayload):
|
|
722
|
+
"""
|
|
723
|
+
DGNSS Broadcast Binary Message
|
|
724
|
+
"""
|
|
725
|
+
msg_type = bit_field(6, int, default=17, signed=False)
|
|
726
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
727
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
728
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
729
|
+
# Note that latitude and longitude are in units of a tenth of a minute
|
|
730
|
+
lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
731
|
+
lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
732
|
+
spare_2 = bit_field(5, bytes, default=b'', is_spare=True)
|
|
733
|
+
data = bit_field(736, bytes, default=b'', variable_length=True)
|
|
734
|
+
|
|
735
|
+
pyais.messages.MessageType17 = HAMessageType17
|
|
736
|
+
pyais.messages.MSG_CLASS[17] = pyais.messages.MessageType17
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
@attr.s(slots=True)
|
|
740
|
+
class HAMessageType19(HAPayload):
|
|
741
|
+
"""
|
|
742
|
+
Extended Class B CS Position Report
|
|
743
|
+
"""
|
|
744
|
+
msg_type = bit_field(6, int, default=19, signed=False)
|
|
745
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
746
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
747
|
+
reserved_1 = bit_field(8, int, default=0)
|
|
748
|
+
|
|
749
|
+
speed = bit_field(10, float, from_converter=from_speed, to_converter=to_speed, default=0, signed=False)
|
|
750
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
751
|
+
lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
752
|
+
lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
753
|
+
course = bit_field(12, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)
|
|
754
|
+
heading = bit_field(9, int, default=0, signed=False)
|
|
755
|
+
second = bit_field(6, int, default=0, signed=False)
|
|
756
|
+
reserved_2 = bit_field(4, int, default=0, signed=False)
|
|
757
|
+
shipname = bit_field(120, str, default='')
|
|
758
|
+
ship_type = bit_field(8, int, default=0, from_converter=ShipType.from_value, to_converter=ShipType.from_value,
|
|
759
|
+
signed=False)
|
|
760
|
+
to_bow = bit_field(9, int, default=0, signed=False)
|
|
761
|
+
to_stern = bit_field(9, int, default=0, signed=False)
|
|
762
|
+
to_port = bit_field(6, int, default=0, signed=False)
|
|
763
|
+
to_starboard = bit_field(6, int, default=0, signed=False)
|
|
764
|
+
epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
|
|
765
|
+
raim = bit_field(1, bool, default=0)
|
|
766
|
+
dte = bit_field(1, bool, default=0)
|
|
767
|
+
assigned = bit_field(1, bool, default=0, signed=False)
|
|
768
|
+
spare_1 = bit_field(4, bytes, default=b'', is_spare=True)
|
|
769
|
+
|
|
770
|
+
pyais.messages.MessageType19 = HAMessageType19
|
|
771
|
+
pyais.messages.MSG_CLASS[19] = pyais.messages.MessageType19
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
@attr.s(slots=True)
|
|
775
|
+
class HAMessageType20ReservationBlock1(HAPayload):
|
|
776
|
+
"""
|
|
777
|
+
Data Link Management Message
|
|
778
|
+
"""
|
|
779
|
+
msg_type = bit_field(6, int, default=20, signed=False)
|
|
780
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
781
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
782
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
783
|
+
|
|
784
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
785
|
+
number1 = bit_field(4, int, default=0, signed=False)
|
|
786
|
+
timeout1 = bit_field(3, int, default=0, signed=False)
|
|
787
|
+
increment1 = bit_field(11, int, default=0, signed=False)
|
|
788
|
+
spare_2 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
789
|
+
|
|
790
|
+
pyais.messages.MessageType20ReservationBlock1 = HAMessageType20ReservationBlock1
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
@attr.s(slots=True)
|
|
794
|
+
class HAMessageType20ReservationBlock12(HAPayload):
|
|
795
|
+
"""
|
|
796
|
+
Data Link Management Message
|
|
797
|
+
"""
|
|
798
|
+
msg_type = bit_field(6, int, default=20, signed=False)
|
|
799
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
800
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
801
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
802
|
+
|
|
803
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
804
|
+
number1 = bit_field(4, int, default=0, signed=False)
|
|
805
|
+
timeout1 = bit_field(3, int, default=0, signed=False)
|
|
806
|
+
increment1 = bit_field(11, int, default=0, signed=False)
|
|
807
|
+
|
|
808
|
+
offset2 = bit_field(12, int, default=0, signed=False)
|
|
809
|
+
number2 = bit_field(4, int, default=0, signed=False)
|
|
810
|
+
timeout2 = bit_field(3, int, default=0, signed=False)
|
|
811
|
+
increment2 = bit_field(11, int, default=0, signed=False)
|
|
812
|
+
spare_2 = bit_field(4, bytes, default=b'', is_spare=True)
|
|
813
|
+
|
|
814
|
+
pyais.messages.MessageType20ReservationBlock12 = HAMessageType20ReservationBlock12
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
@attr.s(slots=True)
|
|
818
|
+
class HAMessageType20ReservationBlock123(HAPayload):
|
|
819
|
+
"""
|
|
820
|
+
Data Link Management Message
|
|
821
|
+
"""
|
|
822
|
+
msg_type = bit_field(6, int, default=20, signed=False)
|
|
823
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
824
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
825
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
826
|
+
|
|
827
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
828
|
+
number1 = bit_field(4, int, default=0, signed=False)
|
|
829
|
+
timeout1 = bit_field(3, int, default=0, signed=False)
|
|
830
|
+
increment1 = bit_field(11, int, default=0, signed=False)
|
|
831
|
+
|
|
832
|
+
offset2 = bit_field(12, int, default=0, signed=False)
|
|
833
|
+
number2 = bit_field(4, int, default=0, signed=False)
|
|
834
|
+
timeout2 = bit_field(3, int, default=0, signed=False)
|
|
835
|
+
increment2 = bit_field(11, int, default=0, signed=False)
|
|
836
|
+
|
|
837
|
+
offset3 = bit_field(12, int, default=0, signed=False)
|
|
838
|
+
number3 = bit_field(4, int, default=0, signed=False)
|
|
839
|
+
timeout3 = bit_field(3, int, default=0, signed=False)
|
|
840
|
+
increment3 = bit_field(11, int, default=0, signed=False)
|
|
841
|
+
spare_2 = bit_field(6, bytes, default=b'', is_spare=True)
|
|
842
|
+
|
|
843
|
+
pyais.messages.MessageType20ReservationBlock123 = HAMessageType20ReservationBlock123
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
@attr.s(slots=True)
|
|
847
|
+
class HAMessageType20ReservationBlock1234(HAPayload):
|
|
848
|
+
"""
|
|
849
|
+
Data Link Management Message
|
|
850
|
+
"""
|
|
851
|
+
msg_type = bit_field(6, int, default=20, signed=False)
|
|
852
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
853
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
854
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
855
|
+
|
|
856
|
+
offset1 = bit_field(12, int, default=0, signed=False)
|
|
857
|
+
number1 = bit_field(4, int, default=0, signed=False)
|
|
858
|
+
timeout1 = bit_field(3, int, default=0, signed=False)
|
|
859
|
+
increment1 = bit_field(11, int, default=0, signed=False)
|
|
860
|
+
|
|
861
|
+
offset2 = bit_field(12, int, default=0, signed=False)
|
|
862
|
+
number2 = bit_field(4, int, default=0, signed=False)
|
|
863
|
+
timeout2 = bit_field(3, int, default=0, signed=False)
|
|
864
|
+
increment2 = bit_field(11, int, default=0, signed=False)
|
|
865
|
+
|
|
866
|
+
offset3 = bit_field(12, int, default=0, signed=False)
|
|
867
|
+
number3 = bit_field(4, int, default=0, signed=False)
|
|
868
|
+
timeout3 = bit_field(3, int, default=0, signed=False)
|
|
869
|
+
increment3 = bit_field(11, int, default=0, signed=False)
|
|
870
|
+
|
|
871
|
+
offset4 = bit_field(12, int, default=0, signed=False)
|
|
872
|
+
number4 = bit_field(4, int, default=0, signed=False)
|
|
873
|
+
timeout4 = bit_field(3, int, default=0, signed=False)
|
|
874
|
+
increment4 = bit_field(11, int, default=0, signed=False)
|
|
875
|
+
|
|
876
|
+
pyais.messages.MessageType20ReservationBlock1234 = HAMessageType20ReservationBlock1234
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
@attr.s(slots=True)
|
|
880
|
+
class HAMessageType20(HAPayload):
|
|
881
|
+
@classmethod
|
|
882
|
+
def create(cls, **kwargs: typing.Union[str, float, int, bool, bytes]) -> "ANY_MESSAGE":
|
|
883
|
+
if 'offset4' in kwargs and int(kwargs['offset4']) > 0:
|
|
884
|
+
return HAMessageType20ReservationBlock1234.create(**kwargs)
|
|
885
|
+
elif 'offset3' in kwargs and int(kwargs['offset3']) > 0:
|
|
886
|
+
return HAMessageType20ReservationBlock123.create(**kwargs)
|
|
887
|
+
elif 'offset2' in kwargs and int(kwargs['offset2']) > 0:
|
|
888
|
+
return HAMessageType20ReservationBlock12.create(**kwargs)
|
|
889
|
+
else:
|
|
890
|
+
return HAMessageType20ReservationBlock1.create(**kwargs)
|
|
891
|
+
|
|
892
|
+
@classmethod
|
|
893
|
+
def from_bitarray(cls, bit_arr: bitarray) -> "ANY_MESSAGE":
|
|
894
|
+
if len(bit_arr) > 136:
|
|
895
|
+
return HAMessageType20ReservationBlock1234.from_bitarray(bit_arr)
|
|
896
|
+
elif len(bit_arr) > 104:
|
|
897
|
+
return HAMessageType20ReservationBlock123.from_bitarray(bit_arr)
|
|
898
|
+
elif len(bit_arr) > 72:
|
|
899
|
+
return HAMessageType20ReservationBlock12.from_bitarray(bit_arr)
|
|
900
|
+
else:
|
|
901
|
+
return HAMessageType20ReservationBlock1.from_bitarray(bit_arr)
|
|
902
|
+
|
|
903
|
+
pyais.messages.MessageType20 = HAMessageType20
|
|
904
|
+
pyais.messages.MSG_CLASS[20] = pyais.messages.MessageType20
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
@attr.s(slots=True)
|
|
908
|
+
class HAMessageType21(HAPayload):
|
|
909
|
+
"""
|
|
910
|
+
Aid-to-Navigation Report
|
|
911
|
+
"""
|
|
912
|
+
msg_type = bit_field(6, int, default=21, signed=False)
|
|
913
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
914
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
915
|
+
|
|
916
|
+
aid_type = bit_field(5, int, default=0, from_converter=NavAid.from_value, to_converter=NavAid.from_value,
|
|
917
|
+
signed=False)
|
|
918
|
+
name = bit_field(120, str, default='')
|
|
919
|
+
|
|
920
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
921
|
+
lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
922
|
+
lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
|
|
923
|
+
to_bow = bit_field(9, int, default=0, signed=False)
|
|
924
|
+
to_stern = bit_field(9, int, default=0, signed=False)
|
|
925
|
+
to_port = bit_field(6, int, default=0, signed=False)
|
|
926
|
+
to_starboard = bit_field(6, int, default=0, signed=False)
|
|
927
|
+
|
|
928
|
+
epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
|
|
929
|
+
second = bit_field(6, int, default=0, signed=False)
|
|
930
|
+
off_position = bit_field(1, bool, default=0)
|
|
931
|
+
reserved_1 = bit_field(8, int, default=0, signed=False)
|
|
932
|
+
raim = bit_field(1, bool, default=0)
|
|
933
|
+
virtual_aid = bit_field(1, bool, default=0)
|
|
934
|
+
assigned = bit_field(1, bool, default=0)
|
|
935
|
+
spare_1 = bit_field(1, bytes, default=b'', is_spare=True)
|
|
936
|
+
name_ext = bit_field(88, str, default='')
|
|
937
|
+
|
|
938
|
+
@functools.cached_property
|
|
939
|
+
def full_name(self) -> str:
|
|
940
|
+
"""The name field is up to 20 characters of 6-bit ASCII. If this field
|
|
941
|
+
is full (has no trailing @ characters) the decoder should interpret
|
|
942
|
+
the Name Extension field later in the message (no more than 14 6-bit
|
|
943
|
+
characters) and concatenate it to this one to obtain the full name."""
|
|
944
|
+
if self.name:
|
|
945
|
+
if self.name_ext:
|
|
946
|
+
return f"{self.name}{self.name_ext}"
|
|
947
|
+
return str(self.name)
|
|
948
|
+
return ""
|
|
949
|
+
|
|
950
|
+
# TODO: replace following override by a generic implementation in Payload.asdict that exports only fields
|
|
951
|
+
def asdict(self, enum_as_int: bool = False) -> typing.Dict[str, typing.Optional[NMEA_VALUE]]:
|
|
952
|
+
res = super().asdict(enum_as_int)
|
|
953
|
+
|
|
954
|
+
# Remove key 'full_name' if present
|
|
955
|
+
if 'full_name' in res:
|
|
956
|
+
del res['full_name']
|
|
957
|
+
|
|
958
|
+
return res
|
|
959
|
+
|
|
960
|
+
pyais.messages.MessageType21 = HAMessageType21
|
|
961
|
+
pyais.messages.MSG_CLASS[21] = pyais.messages.MessageType21
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
@attr.s(slots=True)
|
|
965
|
+
class HAMessageType22Addressed(HAPayload):
|
|
966
|
+
"""
|
|
967
|
+
Channel Management
|
|
968
|
+
"""
|
|
969
|
+
msg_type = bit_field(6, int, default=22, signed=False)
|
|
970
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
971
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
972
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
973
|
+
|
|
974
|
+
channel_a = bit_field(12, int, default=0, signed=False)
|
|
975
|
+
channel_b = bit_field(12, int, default=0, signed=False)
|
|
976
|
+
txrx = bit_field(4, int, default=0, signed=False)
|
|
977
|
+
power = bit_field(1, bool, default=0) # 69 bits
|
|
978
|
+
|
|
979
|
+
# If it is addressed (addressed field is 1),
|
|
980
|
+
# the same span of data is interpreted as two 30-bit MMSIs
|
|
981
|
+
# beginning at bit offsets 69 and 104 respectively.
|
|
982
|
+
dest1 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
983
|
+
empty_1 = bit_field(5, int, default=0)
|
|
984
|
+
dest2 = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
985
|
+
empty_2 = bit_field(5, int, default=0)
|
|
986
|
+
|
|
987
|
+
addressed = bit_field(1, bool, default=0)
|
|
988
|
+
band_a = bit_field(1, bool, default=0)
|
|
989
|
+
band_b = bit_field(1, bool, default=0)
|
|
990
|
+
zonesize = bit_field(3, int, default=0)
|
|
991
|
+
spare_2 = bit_field(23, bytes, default=b'', is_spare=True)
|
|
992
|
+
|
|
993
|
+
pyais.messages.MessageType22Addressed = HAMessageType22Addressed
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
@attr.s(slots=True)
|
|
997
|
+
class HAMessageType22Broadcast(HAPayload):
|
|
998
|
+
"""
|
|
999
|
+
Channel Management
|
|
1000
|
+
"""
|
|
1001
|
+
msg_type = bit_field(6, int, default=22, signed=False)
|
|
1002
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1003
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1004
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
1005
|
+
|
|
1006
|
+
channel_a = bit_field(12, int, default=0, signed=False)
|
|
1007
|
+
channel_b = bit_field(12, int, default=0, signed=False)
|
|
1008
|
+
txrx = bit_field(4, int, default=0, signed=False)
|
|
1009
|
+
power = bit_field(1, bool, default=0)
|
|
1010
|
+
|
|
1011
|
+
# If the message is broadcast (addressed field is 0),
|
|
1012
|
+
# the ne_lon, ne_lat, sw_lon, and sw_lat fields are the
|
|
1013
|
+
# corners of a rectangular jurisdiction area over which control parameter
|
|
1014
|
+
# ne_lon, ne_lat, sw_lon, and sw_lat fields are in 0.1 minutes
|
|
1015
|
+
ne_lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1016
|
+
ne_lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1017
|
+
sw_lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1018
|
+
sw_lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1019
|
+
|
|
1020
|
+
addressed = bit_field(1, bool, default=0)
|
|
1021
|
+
band_a = bit_field(1, bool, default=0)
|
|
1022
|
+
band_b = bit_field(1, bool, default=0)
|
|
1023
|
+
zonesize = bit_field(3, int, default=0, signed=False)
|
|
1024
|
+
spare_2 = bit_field(23, bytes, default=b'', is_spare=True)
|
|
1025
|
+
|
|
1026
|
+
pyais.messages.MessageType22Broadcast = HAMessageType22Broadcast
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
@attr.s(slots=True)
|
|
1030
|
+
class HAMessageType23(HAPayload):
|
|
1031
|
+
"""
|
|
1032
|
+
Group Assignment Command
|
|
1033
|
+
"""
|
|
1034
|
+
msg_type = bit_field(6, int, default=23, signed=False)
|
|
1035
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1036
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1037
|
+
spare_1 = bit_field(2, bytes, default=b'', is_spare=True)
|
|
1038
|
+
|
|
1039
|
+
ne_lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1040
|
+
ne_lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1041
|
+
sw_lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1042
|
+
sw_lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1043
|
+
|
|
1044
|
+
station_type = bit_field(4, int, default=0, from_converter=StationType.from_value,
|
|
1045
|
+
to_converter=StationType.from_value)
|
|
1046
|
+
ship_type = bit_field(8, int, default=0, from_converter=ShipType.from_value, to_converter=ShipType.from_value)
|
|
1047
|
+
spare_2 = bit_field(22, bytes, default=b'', is_spare=True)
|
|
1048
|
+
|
|
1049
|
+
txrx = bit_field(2, int, default=0, from_converter=TransmitMode.from_value, to_converter=TransmitMode.from_value,
|
|
1050
|
+
signed=False)
|
|
1051
|
+
interval = bit_field(4, int, default=0, from_converter=StationIntervals.from_value,
|
|
1052
|
+
to_converter=StationIntervals.from_value)
|
|
1053
|
+
quiet = bit_field(4, int, default=0, signed=False)
|
|
1054
|
+
spare_3 = bit_field(6, bytes, default=b'', is_spare=True)
|
|
1055
|
+
|
|
1056
|
+
pyais.messages.MessageType23 = HAMessageType23
|
|
1057
|
+
pyais.messages.MSG_CLASS[23] = pyais.messages.MessageType23
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
@attr.s(slots=True)
|
|
1061
|
+
class HAMessageType24PartA(HAPayload):
|
|
1062
|
+
msg_type = bit_field(6, int, default=24, signed=False)
|
|
1063
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1064
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1065
|
+
|
|
1066
|
+
# partno = bit_field(2, int, default=0, signed=False)
|
|
1067
|
+
reserved = bit_field(1, int, default=0, signed=False)
|
|
1068
|
+
partno = bit_field(1, int, default=0, signed=False)
|
|
1069
|
+
shipname = bit_field(120, str, default='')
|
|
1070
|
+
spare_1 = bit_field(8, bytes, default=b'', is_spare=True)
|
|
1071
|
+
|
|
1072
|
+
pyais.messages.MessageType24PartA = HAMessageType24PartA
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
@attr.s(slots=True)
|
|
1076
|
+
class HAMessageType24PartB(HAPayload):
|
|
1077
|
+
msg_type = bit_field(6, int, default=24, signed=False)
|
|
1078
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1079
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1080
|
+
|
|
1081
|
+
partno = bit_field(2, int, default=0, signed=False)
|
|
1082
|
+
ship_type = bit_field(8, int, default=0, signed=False)
|
|
1083
|
+
vendorid = bit_field(18, str, default='', signed=False)
|
|
1084
|
+
model = bit_field(4, int, default=0, signed=False)
|
|
1085
|
+
serial = bit_field(20, int, default=0, signed=False)
|
|
1086
|
+
callsign = bit_field(42, str, default='')
|
|
1087
|
+
|
|
1088
|
+
to_bow = bit_field(9, int, default=0, signed=False)
|
|
1089
|
+
to_stern = bit_field(9, int, default=0, signed=False)
|
|
1090
|
+
to_port = bit_field(6, int, default=0, signed=False)
|
|
1091
|
+
to_starboard = bit_field(6, int, default=0, signed=False)
|
|
1092
|
+
|
|
1093
|
+
spare_1 = bit_field(6, bytes, default=b'', is_spare=True)
|
|
1094
|
+
|
|
1095
|
+
pyais.messages.MessageType24PartB = HAMessageType24PartB
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
@attr.s(slots=True)
|
|
1099
|
+
class HAMessageType25AddressedStructured(HAPayload):
|
|
1100
|
+
msg_type = bit_field(6, int, default=25, signed=False)
|
|
1101
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1102
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1103
|
+
|
|
1104
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1105
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1106
|
+
|
|
1107
|
+
dest_mmsi = bit_field(30, int, default=0, from_converter=from_mmsi, signed=False)
|
|
1108
|
+
spare = bit_field(2, int, default=0, signed=False, is_spare=True)
|
|
1109
|
+
app_id = bit_field(16, int, default=0, signed=False)
|
|
1110
|
+
data = bit_field(80, bytes, default=b'', variable_length=True)
|
|
1111
|
+
|
|
1112
|
+
pyais.messages.MessageType25AddressedStructured = HAMessageType25AddressedStructured
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
@attr.s(slots=True)
|
|
1116
|
+
class HAMessageType25AddressedUnstructured(HAPayload):
|
|
1117
|
+
msg_type = bit_field(6, int, default=25, signed=False)
|
|
1118
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1119
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1120
|
+
|
|
1121
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1122
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1123
|
+
|
|
1124
|
+
dest_mmsi = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
1125
|
+
spare = bit_field(2, int, default=0, signed=False, is_spare=True)
|
|
1126
|
+
data = bit_field(96, bytes, default=b'', variable_length=True)
|
|
1127
|
+
|
|
1128
|
+
pyais.messages.MessageType25AddressedUnstructured = HAMessageType25AddressedUnstructured
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
@attr.s(slots=True)
|
|
1132
|
+
class HAMessageType26AddressedStructured(HAPayload, CommunicationStateMixin):
|
|
1133
|
+
msg_type = bit_field(6, int, default=26, signed=False)
|
|
1134
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1135
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1136
|
+
|
|
1137
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1138
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1139
|
+
|
|
1140
|
+
dest_mmsi = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
1141
|
+
spare1 = bit_field(2, int, default=0, signed=False, is_spare=True)
|
|
1142
|
+
app_id = bit_field(16, int, default=0, signed=False)
|
|
1143
|
+
data = bit_field(952, bytes, default=b'', variable_length=True)
|
|
1144
|
+
spare2 = bit_field(4, int, default=0, signed=False, is_spare=True)
|
|
1145
|
+
radio = bit_field(20, int, default=0, signed=False)
|
|
1146
|
+
|
|
1147
|
+
pyais.messages.MessageType26AddressedStructured = HAMessageType26AddressedStructured
|
|
1148
|
+
|
|
1149
|
+
|
|
1150
|
+
@attr.s(slots=True)
|
|
1151
|
+
class HAMessageType26BroadcastStructured(HAPayload, CommunicationStateMixin):
|
|
1152
|
+
msg_type = bit_field(6, int, default=26, signed=False)
|
|
1153
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1154
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1155
|
+
|
|
1156
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1157
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1158
|
+
|
|
1159
|
+
app_id = bit_field(16, int, default=0, signed=False)
|
|
1160
|
+
data = bit_field(984, bytes, default=b'', variable_length=True)
|
|
1161
|
+
spare2 = bit_field(4, int, default=0, signed=False, is_spare=True)
|
|
1162
|
+
radio = bit_field(20, int, default=0, signed=False)
|
|
1163
|
+
|
|
1164
|
+
pyais.messages.MessageType26BroadcastStructured = HAMessageType26BroadcastStructured
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
@attr.s(slots=True)
|
|
1168
|
+
class HAMessageType26AddressedUnstructured(HAPayload, CommunicationStateMixin):
|
|
1169
|
+
msg_type = bit_field(6, int, default=26, signed=False)
|
|
1170
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1171
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1172
|
+
|
|
1173
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1174
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1175
|
+
|
|
1176
|
+
dest_mmsi = bit_field(30, int, default=0, from_converter=from_mmsi)
|
|
1177
|
+
spare1 = bit_field(2, int, default=0, signed=False, is_spare=True)
|
|
1178
|
+
data = bit_field(968, bytes, default=b'', variable_length=True)
|
|
1179
|
+
spare2 = bit_field(4, int, default=0, signed=False, is_spare=True)
|
|
1180
|
+
radio = bit_field(20, int, default=0, signed=False)
|
|
1181
|
+
|
|
1182
|
+
pyais.messages.MessageType26AddressedUnstructured = HAMessageType26AddressedUnstructured
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
@attr.s(slots=True)
|
|
1186
|
+
class HAMessageType26BroadcastUnstructured(HAPayload, CommunicationStateMixin):
|
|
1187
|
+
msg_type = bit_field(6, int, default=26, signed=False)
|
|
1188
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1189
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1190
|
+
|
|
1191
|
+
addressed = bit_field(1, bool, default=0, signed=False)
|
|
1192
|
+
structured = bit_field(1, bool, default=0, signed=False)
|
|
1193
|
+
|
|
1194
|
+
data = bit_field(1000, bytes, default=b'', variable_length=True)
|
|
1195
|
+
spare2 = bit_field(4, int, default=0, signed=False, is_spare=True)
|
|
1196
|
+
radio = bit_field(20, int, default=0, signed=False)
|
|
1197
|
+
|
|
1198
|
+
pyais.messages.MessageType26BroadcastUnstructured = HAMessageType26BroadcastUnstructured
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
class HAMessageType26(HAPayload):
|
|
1202
|
+
"""
|
|
1203
|
+
Multiple Slot Binary Message
|
|
1204
|
+
|
|
1205
|
+
NOTE: This message type is quite uncommon and
|
|
1206
|
+
I was not able find any real world occurrence of the type.
|
|
1207
|
+
Also documentation seems to vary. Use with caution.
|
|
1208
|
+
"""
|
|
1209
|
+
|
|
1210
|
+
@classmethod
|
|
1211
|
+
def create(cls, **kwargs: typing.Union[str, float, int, bool, bytes]) -> "ANY_MESSAGE":
|
|
1212
|
+
addressed = kwargs.get('addressed', False)
|
|
1213
|
+
structured = kwargs.get('structured', False)
|
|
1214
|
+
|
|
1215
|
+
if addressed:
|
|
1216
|
+
if structured:
|
|
1217
|
+
return HAMessageType26AddressedStructured.create(**kwargs)
|
|
1218
|
+
else:
|
|
1219
|
+
return HAMessageType26AddressedUnstructured.create(**kwargs)
|
|
1220
|
+
else:
|
|
1221
|
+
if structured:
|
|
1222
|
+
return HAMessageType26BroadcastStructured.create(**kwargs)
|
|
1223
|
+
else:
|
|
1224
|
+
return HAMessageType26BroadcastUnstructured.create(**kwargs)
|
|
1225
|
+
|
|
1226
|
+
@classmethod
|
|
1227
|
+
def from_bitarray(cls, bit_arr: bitarray) -> "ANY_MESSAGE":
|
|
1228
|
+
addressed: int = get_int(bit_arr, 38, 39)
|
|
1229
|
+
structured: int = get_int(bit_arr, 39, 40)
|
|
1230
|
+
|
|
1231
|
+
if addressed:
|
|
1232
|
+
if structured:
|
|
1233
|
+
return HAMessageType26AddressedStructured.from_bitarray(bit_arr)
|
|
1234
|
+
else:
|
|
1235
|
+
return HAMessageType26AddressedUnstructured.from_bitarray(bit_arr)
|
|
1236
|
+
else:
|
|
1237
|
+
if structured:
|
|
1238
|
+
return HAMessageType26BroadcastStructured.from_bitarray(bit_arr)
|
|
1239
|
+
else:
|
|
1240
|
+
return HAMessageType26BroadcastUnstructured.from_bitarray(bit_arr)
|
|
1241
|
+
|
|
1242
|
+
pyais.messages.MessageType26 = HAMessageType26
|
|
1243
|
+
pyais.messages.MSG_CLASS[26] = pyais.messages.MessageType26
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
@attr.s(slots=True)
|
|
1247
|
+
class HAMessageType27(HAPayload):
|
|
1248
|
+
"""
|
|
1249
|
+
Long Range AIS Broadcast message
|
|
1250
|
+
"""
|
|
1251
|
+
msg_type = bit_field(6, int, default=27, signed=False)
|
|
1252
|
+
repeat = bit_field(2, int, default=0, signed=False)
|
|
1253
|
+
mmsi = bit_field(30, int, from_converter=from_mmsi)
|
|
1254
|
+
|
|
1255
|
+
accuracy = bit_field(1, bool, default=0, signed=False)
|
|
1256
|
+
raim = bit_field(1, bool, default=0, signed=False)
|
|
1257
|
+
status = bit_field(4, int, default=0, from_converter=NavigationStatus, to_converter=NavigationStatus, signed=False)
|
|
1258
|
+
lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1259
|
+
lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
|
|
1260
|
+
speed = bit_field(6, float, default=0, signed=False)
|
|
1261
|
+
course = bit_field(9, float, default=0, signed=False)
|
|
1262
|
+
gnss = bit_field(1, bool, default=0, signed=False)
|
|
1263
|
+
spare_1 = bit_field(1, bytes, default=b'', is_spare=True)
|
|
1264
|
+
|
|
1265
|
+
pyais.messages.MessageType27 = HAMessageType27
|
|
1266
|
+
pyais.messages.MSG_CLASS[27] = pyais.messages.MessageType27
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
# Manage tag blocks
|
|
1270
|
+
|
|
1271
|
+
class HATagBlock(TagBlock):
|
|
1272
|
+
def __init__(self, raw: bytes = None) -> None:
|
|
1273
|
+
super().__init__(raw)
|
|
1274
|
+
|
|
1275
|
+
def to_raw(self):
|
|
1276
|
+
fields = []
|
|
1277
|
+
if self._group is not None:
|
|
1278
|
+
fields.append(f"g:{self._group}")
|
|
1279
|
+
if self._source_station is not None:
|
|
1280
|
+
fields.append(f"s:{self._source_station}")
|
|
1281
|
+
if self._receiver_timestamp is not None:
|
|
1282
|
+
fields.append(f"c:{self._receiver_timestamp}")
|
|
1283
|
+
if self._destination_station is not None:
|
|
1284
|
+
fields.append(f"d:{self._destination_station}")
|
|
1285
|
+
if self._line_count is not None:
|
|
1286
|
+
fields.append(f"n:{self._line_count}")
|
|
1287
|
+
if self._relative_time is not None:
|
|
1288
|
+
fields.append(f"r:{self._relative_time}")
|
|
1289
|
+
if self._text is not None:
|
|
1290
|
+
fields.append(f"t:{self._text}")
|
|
1291
|
+
|
|
1292
|
+
payload_str = ','.join(fields)
|
|
1293
|
+
payload = payload_str.encode()
|
|
1294
|
+
|
|
1295
|
+
chk_int = checksum(payload)
|
|
1296
|
+
chk = f"{chk_int:02X}".encode()
|
|
1297
|
+
|
|
1298
|
+
return payload + ASTERISK + chk
|
|
1299
|
+
|
|
1300
|
+
pyais.messages.TagBlock = HATagBlock
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
|