



  

<!DOCTYPE html>
<html
  lang="en"
  
  data-color-mode="auto" data-light-theme="light" data-dark-theme="dark"
  data-a11y-animated-images="system" data-a11y-link-underlines="true"
  
  >




  <head>
    <meta charset="utf-8">
  <link rel="dns-prefetch" href="https://github.githubassets.com">
  <link rel="dns-prefetch" href="https://avatars.githubusercontent.com">
  <link rel="dns-prefetch" href="https://github-cloud.s3.amazonaws.com">
  <link rel="dns-prefetch" href="https://user-images.githubusercontent.com/">
  <link rel="preconnect" href="https://github.githubassets.com" crossorigin>
  <link rel="preconnect" href="https://avatars.githubusercontent.com">

<script type="importmap">{"imports":{"react":"https://github.githubassets.com/assets/react-e27d1b3e03961e68.js","react-dom":"https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js","react-dom/client":"https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js","react-is":"https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js","react-reconciler":"https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js","react/compiler-runtime":"https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js","react/jsx-dev-runtime":"https://github.githubassets.com/assets/react-jsx-dev-runtime-ea55d68667d559e5.js","react/jsx-runtime":"https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js","scheduler":"https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js"}}</script>
<meta name="react-profiling" content="0" data-turbo-transient="true" />
<meta name="react-import-map" content="react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@777f63f87cd0" data-turbo-track="reload" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-e27d1b3e03961e68.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-compiler-runtime-4610bd6d3de9c049.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/scheduler-58b860b049ca307c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-e5fd46a22d5c4058.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-dom-client-1b4a3ee065998cea.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-is-e0b593954b4706d8.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-jsx-runtime-4915cb0f5b3aff04.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-reconciler-8e99e505c4429605.js" />

  


  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="light" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light-5c4e9fc574bf49f3.css" /><link data-color-theme="light_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_high_contrast-fb37c309e0603a69.css" /><link data-color-theme="light_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind-0f910d8806d5761b.css" /><link data-color-theme="light_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_colorblind_high_contrast-a7abd4d4c49ac593.css" /><link data-color-theme="light_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia-5fc4c4a9cb41e596.css" /><link data-color-theme="light_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/light_tritanopia_high_contrast-ed0cee9214dedabd.css" /><link data-color-theme="dark" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark-afff6c53aef9b9d1.css" /><link data-color-theme="dark_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_high_contrast-c409873d7987dffa.css" /><link data-color-theme="dark_colorblind" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind-78a2491d538dab5a.css" /><link data-color-theme="dark_colorblind_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_colorblind_high_contrast-c26b63d7c532d0a2.css" /><link data-color-theme="dark_tritanopia" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia-a6f60cea68c08405.css" /><link data-color-theme="dark_tritanopia_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_tritanopia_high_contrast-f66faf28b2ea18b6.css" /><link data-color-theme="dark_dimmed" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed-6701f6218c53cf5b.css" /><link data-color-theme="dark_dimmed_high_contrast" crossorigin="anonymous" media="all" rel="stylesheet" data-href="https://github.githubassets.com/assets/dark_dimmed_high_contrast-7037fa46ae124d97.css" />

  <style type="text/css">
    :root {
      --tab-size-preference: 4;
    }

    pre, code {
      tab-size: var(--tab-size-preference);
    }
  </style>

    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-primitives-97df7784617ce1ea.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-9be9fe6313f476af.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-62747e27e61258dc.css" />
    <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-b04ca6fccda778e2.css" />
  <link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/repository-72bd7d974b5ac1cc.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-4c5c0895f723f870.css" />

  

  <script type="application/json" id="client-env">{"locale":"en","featureFlags":["actions_caches_react_shell","actions_enable_background_steps","actions_new_hosted_runner_image_select_sizes_and_versions","actions_runners_react_shell","agent_author_search_expansion","agent_author_search_expansion_ui_pulls","alternate_user_config_repo","async_conversion_coverage_enabled","billing_billable_licenses_cost_center_bucket_fix","billing_budget_expiration","billing_cost_center_list_assigned_resources","billing_discount_threshold_notification","code_quality_enablement_banner_targeting","code_quality_remove_preview","code_view_raf_sticky_lines","codespaces_prebuild_region_target_update","coding_agent_third_party_model_ui","copilot_agent_snippy","copilot_ahp_tool_call_timing","copilot_api_agentic_issue_marshal_yaml","copilot_automation_repo_mcp_servers","copilot_automations_pagination","copilot_base_model_policy_row","copilot_chat_auto_mode_v2","copilot_chat_clear_model_selection_for_default_change","copilot_chat_early_task_provisioning","copilot_chat_persist_session_drafts","copilot_chat_vision_dotcom_chat_ga_gate","copilot_css_textarea_autosize","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_removal_on_marketplace","copilot_fix_failed_workflows_all_skus","copilot_hide_hovercard","copilot_immersive_task_hyperlinking","copilot_mc_cli_resume_any_users_task","copilot_mission_control_agent_merge_fix_ci","copilot_mission_control_agent_merge_resolve_conflicts","copilot_mission_control_awps_batching","copilot_mission_control_early_stop","copilot_mission_control_managed_sandbox_environments","copilot_mission_control_needs_attention","copilot_mission_control_reasoning_effort","copilot_mission_control_sandbox_remote_bypass","copilot_mission_control_task_alive_updates","copilot_mission_control_task_sharing","copilot_org_policy_page_focus_mode","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_swe_agent_authorization_status_ui","copilot_swe_agent_automation_resource_scoped_writes","copilot_swe_agent_discussion_comment_trigger","copilot_swe_agent_discussion_opened_trigger","copilot_swe_agent_discussion_updated_trigger","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_issue_assigned_trigger","copilot_swe_agent_issue_comment_trigger","copilot_swe_agent_issue_labeled_trigger","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_pull_request_assigned_trigger","copilot_swe_agent_pull_request_comment_trigger","copilot_swe_agent_pull_request_labeled_trigger","copilot_swe_agent_pull_request_merged_trigger","copilot_swe_agent_pull_request_opened_trigger","copilot_swe_agent_pull_request_ready_for_review_trigger","copilot_swe_agent_pull_request_review_requested_trigger","copilot_swe_agent_pull_request_review_submitted_trigger","copilot_swe_agent_pull_request_synchronize_trigger","copilot_swe_agent_sub_issue_added_trigger","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_task_scoped_alive_channel","copilot_token_based_billing","copilot_unconfigured_is_inherited","copilot_user_can_upgrade_plan_field","copilot_web_integration_cutover","copilot_workbench_sunset_redirect","dashboard_agents_module_auth_token_check","dashboard_indexeddb_caching","fgpat_permissions_selector_redesign","glc_code_quality_repo_settings_workflow_config","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","in_product_messaging_datadog_monitoring","ipm_ubb_individual_budget_banner","issue_fields_multi_select","issue_inline_avatars","issue_pinned_views","issue_pinned_views_team_vs_personal","issue_relative_time_micro","issue_viewer_paved_path","issues_expanded_file_types","issues_hide_closed_sub_issues","issues_lazy_load_comment_box_suggestions","issues_react_chrome_container_query_fix","labels_archiving","labels_archiving_info","landing_pages_ninetailed","lifecycle_label_name_updates","marketing_cookie_consent_banner","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_queue_restricted_pushers_warning","merge_status_header_feedback","milestone_closed_issues_prioritization","octocaptcha_origin_optimization","primer_react_css_anchor_positioning","primer_react_merged_forwarded_refs","primer_react_timeline_list_semantics","prs_copilot_app_open_action","prs_css_anchor_positioning","pull_request_copilot_attribution_header","pull_request_overview_merge_control","pull_request_overview_panel_edit_description","pull_request_persister","pull_request_stacks_navigation_shortcuts","pull_request_virtualization_image_estimate","pull_request_virtualization_loader_batching","pull_request_virtualization_scroll_compensation","pull_request_virtualization_scroll_intent","quick_search_lazy_suggestions","react_blob_isolate_code_lines","react_blob_ssr_content_visibility","react_data_router_tanstack_allowed","react_logged_out_repository_header","react_query_props_with_key","react_sandbox_future_tanstack","repo_app_turbo","repo_issues_sidebar_layout","repo_pulls_dashboard_declutter","repo_pulls_dashboard_ga","repo_pulls_dashboard_persistence","repos_contributors_limited_default_range","review_involves_filter","rule_ignored_file_paths","rulesets_actor_list_editor","sample_network_conn_type","security_center_artifact_filters_popover","see_who_reacted","semantic_similarity_duplicate_issue_detection","session_logs_ungroup_reasoning_text","set_sha256_on_repo_creation_form","site_banner_desktop_copilot_app","site_ghca_pixel_mona","site_github_app_ga_page","site_github_app_ga_page_highlight","site_github_app_mobile_native_share","site_global_banner_dev_days_attendee","site_global_nav_spark_models_removed","speculation_rules_ui_service","suggest_custom_property_values_copilot","suppress_automated_browser_vitals","swp_forms_disable_octocaptcha","thread_resolution_reason","update_issue_suggestions","viewscreen_sandbox","warn_inaccessible_attachments","webp_support","workstream_plugin_bootstrap"],"githubDomain":"https://github.com","copilotApiOverrideUrl":"https://api.githubcopilot.com","cmcApiUrl":"https://api.github.com/cmc_internal/api"}</script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/high-contrast-cookie-3663bcaacc724f4c.js"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wp-runtime-721126455a2225e3.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-foundation-156c6259e31cc551.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-runtime-f6ce38f47c2e1d56.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fetch-utilities-804a14996977c014.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ser-fb5d13278eb42203.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tp-ea613009a991e211.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/environment-107a7753389a8a2d.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-runtime.25915bc98fb322a6.module.css" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/catalyst-52ed81112a548e10.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/selector-observer-e8810f64c443fb9b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/relative-time-element-fe7e72f699fdf493.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/by-9da36843ba9d191d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ja9-9a1160b939f2cd52.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/3h-55903cf2303047ad.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hk-0d35a1220587d888.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jz5-c964b1a3671f2a92.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hj-1d800d09e9a8720a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j0-1ad12374da9777f2.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/github-elements-531d35538b61fd0d.js" defer="defer"></script>
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/element-registry-bf5e3b2aee197122.js" defer="defer"></script>
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/runtime-helpers-5e0b3ae3c4036207.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/aria-live-752acdc868e2da64.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hotkey-e87ffadced20a3c9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/react-core-75bc55d9971828b7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a9-34620c7b13885011.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ur-643d2814115561a9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4t6-5d0869970ac0aac0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pb0-a5c5ec36d311aeef.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/6n-afcf86ccabc4cad2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zj-f08c728cf00018aa.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/o6-2a1c68d269710ee9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/h9a-c6405aa090de0e84.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/vy6-3c3e6e96629c2ce4.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/js-4eb97d5803e657c0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/g7-baf9e817ae102b01.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/unc-96389cade7bad821.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/nu-65322a6997c6c93c.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/behaviors-5dd3809b5c8bac3c.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/react-core.3792087592bf207a.module.css" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-menu-4318f0f4c608f3dd.js" defer="defer"></script>
  
  <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/primer-react-7afef75d393cf294.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/op-acddf38478d45ef9.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ncx-461e4b09f2e67ee5.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/qmp-c7cfba40cf5a93b0.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j6-148a74299e5a01ab.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/t7l-a52ba56147f77909.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4u-ec4874f70bfd5d76.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/s0-481bd34f90bc2bd7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/y5-ed17e73b4c0bd15e.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/n4-be595475cdbf7d4b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j8-23a3c5ff8eef412d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/la-27679061f16e1495.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/iu-9806a0291a782cbe.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ftx-39fb490e3c1d778f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/zi-32a0c3c081ea5cce.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sg-7a5d50475874c0e1.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pd-6a8710840033e042.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/86g-ec28c25ed7d5de06.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sn-1bab7384818953b7.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/iq-6d4ecf6d2fb6993c.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/p2-7d8e7cee5b46cb45.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7d-2e3cc25d719f3a95.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/4d-76d8acdf21a33266.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/tsb-e0f6288ab985d9d2.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/lf-910ef220ce85c660.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/2j-2f19c1afd2ceaf93.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/me-f1476ec44a29f34a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/p2u-e654a57b1a70c47d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/5d-d97cc006d4da4d9b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/r5-38f013bc2c0fd298.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/r6s-9039f5619230a653.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/9e-cece06724bf6ea3b.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/x9-3e077a3a37a73f9f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/pj-87629cb5389d7663.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/eq-3764fd5f66494289.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/13-d889936dff7e5c0f.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/44-8193eea4af825ead.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/v4-9c1327d212e52a0d.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/id-7f39706ba9007db6.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ah-271362a1ed227d03.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/s1g-d434d712bca59d3a.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/vz-d7f4a583896f872e.js" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/47-c8c0ce2700acc5be.js" />
<script crossorigin="anonymous" type="module" src="https://github.githubassets.com/assets/code-view-4118a687d0150749.js" defer="defer"></script>
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/pj.75627bfeb9ceb1da.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/ah.2623f7c6ecbe2ecd.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/47.78770c57a48a810c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/code-view.37656c95a9729fb9.module.css" />


  <title>scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile · GitHub</title>



  <meta name="route-pattern" content="/:user_id/:repository/blob/*name(/*path)" data-turbo-transient>
  <meta name="route-controller" content="blob" data-turbo-transient>
  <meta name="route-action" content="show" data-turbo-transient>
  <meta name="fetch-nonce" content="v2:159a4cfa-a366-3ad7-9883-b96acf73c67d">

    
  <meta name="current-catalog-service-hash" content="f3abb0cc802f3d7b95fc8762b94bdcb13bf39634c40c357301c4aa1d67a256fb">


  <meta name="request-id" content="AC16:4569F:AF67B3F:E45E826:6ACA5D0F" data-pjax-transient="true"/><meta name="html-safe-nonce" content="92710c825e8e712e29836a542a4782baf9ece8feed365f7497735770978ed15d" data-pjax-transient="true"/><meta name="visitor-payload" content="eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBQzE2OjQ1NjlGOkFGNjdCM0Y6RTQ1RTgyNjo2QUNBNUQwRiIsInZpc2l0b3JfaWQiOiIyNTc3NTQ4Mjc3MTQ3MzkzMjk1IiwicmVnaW9uX2VkZ2UiOiJmcmEiLCJyZWdpb25fcmVuZGVyIjoiZnJhIn0=" data-pjax-transient="true"/><meta name="visitor-hmac" content="2058dfb2ed3a9b3857a87cbb7aa816ffc1b629eebb443815caf0fb9d1816f7be" data-pjax-transient="true"/>


    <meta name="hovercard-subject-tag" content="repository:1078098970" data-turbo-transient>


  <meta name="github-keyboard-shortcuts" content="repository,source-code,file-tree,copilot" data-turbo-transient="true" />
  

  <meta name="selected-link" value="repo_source" data-turbo-transient>
  <link rel="assets" href="https://github.githubassets.com/">

    <meta name="google-site-verification" content="Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I">

<meta name="octolytics-url" content="https://collector.github.com/github/collect" />





  <meta name="analytics-location" content="/&lt;user-name&gt;/&lt;repo-name&gt;/blob/show" data-turbo-transient="true" />

  




    <meta name="user-login" content="">

  

    <meta name="viewport" content="width=device-width">

    

      <meta name="description" content="Модуль выполнения бизнес-логики для мобильного приложения SCADA - scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile">

      <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">

    <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
    <meta property="fb:app_id" content="1401488693436528">
    <meta name="apple-itunes-app" content="app-id=1477376905, app-argument=https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md" />

      <meta name="twitter:image" content="https://opengraph.githubassets.com/71676db376dfb8d2fe24218725ff0b614f54579d7317362c5102abd7a90041da/savushkin-dev/scada-mobile" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile" /><meta name="twitter:description" content="Модуль выполнения бизнес-логики для мобильного приложения SCADA - savushkin-dev/scada-mobile" />
  <meta property="og:image" content="https://opengraph.githubassets.com/71676db376dfb8d2fe24218725ff0b614f54579d7317362c5102abd7a90041da/savushkin-dev/scada-mobile" /><meta property="og:image:alt" content="Модуль выполнения бизнес-логики для мобильного приложения SCADA - savushkin-dev/scada-mobile" /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile" /><meta property="og:url" content="https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md" /><meta property="og:description" content="Модуль выполнения бизнес-логики для мобильного приложения SCADA - savushkin-dev/scada-mobile" />
  




      <meta name="hostname" content="github.com">



        <meta name="expected-hostname" content="github.com">


  <meta http-equiv="x-pjax-version" content="d05be8f8b6905b6460fc264e80447721dfee1e32cf9e4291c4bf58f160c9f705" data-turbo-track="reload">
  <meta http-equiv="x-pjax-csp-version" content="c4a65e47b0c850e1157ae8e66298070851d7e3b558038de5a3b4ff7a93e630ed" data-turbo-track="reload">
  <meta http-equiv="x-pjax-css-version" content="a80b0545b169f440b929ce2b977c76b6988f014cca6513fb83c5df6581635fb3" data-turbo-track="reload">
  <meta http-equiv="x-pjax-js-version" content="af51c6f9d659be0324665373df3ee542f817dbd7f66f352a087935f2ceabe545" data-turbo-track="reload">

  <meta name="turbo-cache-control" content="no-preview" data-turbo-transient="">

      <meta name="turbo-cache-control" content="no-cache" data-turbo-transient>

    <meta data-hydrostats="publish">

  <meta name="go-import" content="github.com/savushkin-dev/scada-mobile git https://github.com/savushkin-dev/scada-mobile.git">

  <meta name="octolytics-dimension-user_id" content="220682001" /><meta name="octolytics-dimension-user_login" content="savushkin-dev" /><meta name="octolytics-dimension-repository_id" content="1078098970" /><meta name="octolytics-dimension-repository_nwo" content="savushkin-dev/scada-mobile" /><meta name="octolytics-dimension-repository_public" content="true" /><meta name="octolytics-dimension-repository_is_fork" content="false" /><meta name="octolytics-dimension-repository_network_root_id" content="1078098970" /><meta name="octolytics-dimension-repository_network_root_nwo" content="savushkin-dev/scada-mobile" />
  



    

    <meta name="turbo-body-classes" content="logged-out env-production page-responsive">
  <meta name="disable-turbo" content="false">


  <meta name="browser-stats-url" content="https://api.github.com/_private/browser/stats">


  <meta name="browser-errors-url" content="https://api.github.com/_private/browser/errors">

  <meta name="release" content="3b1bb5b10f5c7fd1769d56e38b15dfddb34052a8" data-turbo-track="reload">
  <meta name="ui-target" content="canary-1">

  <link rel="mask-icon" href="https://github.githubassets.com/assets/pinned-octocat-093da3e6fa40.svg" color="#000000">
  <link rel="alternate icon" class="js-site-favicon" type="image/png" href="https://github.githubassets.com/favicons/favicon.png">
  <link rel="icon" class="js-site-favicon" type="image/svg+xml" href="https://github.githubassets.com/favicons/favicon.svg" data-base-href="https://github.githubassets.com/favicons/favicon">

<meta name="theme-color" content="#1e2327">
<meta name="color-scheme" content="light dark" />


  <link rel="manifest" href="/manifest.json" crossOrigin="use-credentials">

  </head>

  <body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
    <div data-turbo-body class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
      <div id="__primerPortalRoot__" style="z-index: 1000; position: absolute; width: 100%;" data-turbo-permanent></div>
      

    <div class="position-relative header-wrapper js-header-wrapper ">
      <a href="#start-of-content" data-skip-target-assigned="false" class="px-2 tmp-py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a>

      <span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full">
    <span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span>
</span>      
      <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/19-c660652a0a1639be.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog-47c5223d8d1af8ef.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/keyboard-shortcuts-dialog.d53d993320d57e14.module.css" />

<react-partial
  partial-name="keyboard-shortcuts-dialog"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>





      

          <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/app-install-banner-partial-3c87deb7f0f20434.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/app-install-banner-partial.7b357012e82003e7.module.css" />

<react-partial
  partial-name="app-install-banner-partial"
  data-ssr="false"
  data-attempted-ssr="false"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{}}</script>
  <div data-target="react-partial.reactRoot"></div>
</react-partial>


          

                <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/et9-7eeec247c287fca4.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/94v-e20946127faa8f6e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fz5-892a9e99cc92858d.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/jag-441e702bfd4c7904.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/hw-8ce7888b57d0a23c.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/rq-0ec82a12cf538a4d.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/1m-a7a4c022483b2713.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/marketing-header-70703fe17422eb58.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-brand-css.fbc35b3d20e988ff.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/1m.fec8b575d0e5234f.module.css" />

<react-partial
  partial-name="marketing-header"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"color_mode":"dark","logged_in":false,"marketing_page":false,"home_path":"/","login_path":"/login?return_to=https%3A%2F%2Fgithub.com%2Fsavushkin-dev%2Fscada-mobile%2Fblob%2Fmain%2FFRONTEND_DATA_SOURCES.md","signup_path":"/signup?ref_cta=Sign+up\u0026ref_loc=header+logged+out\u0026ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow\u0026source=header-repo\u0026source_repo=savushkin-dev%2Fscada-mobile","signup_enabled":true,"is_signup_controller":false,"show_search_and_nav":true,"hide_search":false,"private_mode_enabled":false,"should_use_dotcom_links":true,"auth_hydro_click":"{\"event_type\":\"authentication.click\",\"payload\":{\"location_in_page\":\"site header menu\",\"repository_id\":null,\"auth_type\":\"SIGN_UP\",\"originating_url\":\"https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md\",\"user_id\":null}}","auth_hydro_click_hmac":"41c8f37a7cbd88937435d6f7244f956911e76e3b4b106d521f46333982758c51","overlay":false,"fixed":false}}</script>
  <div data-target="react-partial.reactRoot"><div data-color-mode="dark" data-light-theme="light" data-dark-theme="dark"><header class="MarketingHeader-module__root__Tk7n3 HeaderMktg header-logged-out" role="banner" data-marketing-header="true" data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" data-is-top="true"><h2 class="MarketingHeader-module__visuallyHidden__sqKsl">Navigation Menu</h2><button type="button" class="MarketingHeader-module__backdrop__sw4RU" aria-label="Close navigation menu"></button><div class="MarketingHeader-module__bar__mBSyE"><div class="MarketingHeader-module__topRow__yeury"><div class="MarketingHeader-module__toggleSlot__hDxbh"><button type="button" class="HeaderMenuToggle-module__toggle__i8EiC" aria-label="Toggle navigation" aria-expanded="false"><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span><span class="HeaderMenuToggle-module__toggleBar__jVN0H"></span></button></div><a href="/" aria-label="Homepage" class="HeaderLogo-module__logo__UFyHI" data-analytics-event="{&quot;action&quot;:&quot;homepage&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;logo&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;homepage_link_logo_header&quot;}"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github" viewBox="0 0 24 24" width="32" height="32" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path></svg></a><div class="AuthCTAs-module__mobileActions__NNzeV"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/login?return_to=https%3A%2F%2Fgithub.com%2Fsavushkin-dev%2Fscada-mobile%2Fblob%2Fmain%2FFRONTEND_DATA_SOURCES.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="41c8f37a7cbd88937435d6f7244f956911e76e3b4b106d521f46333982758c51"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_3dd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_3dd_">Appearance settings</div></div></div><div class="MarketingHeader-module__menu__GIy3y"><div class="MarketingHeader-module__menuWrapper__owstH"><nav class="MarketingNavigation-module__nav__W0KYY" aria-label="Global"><ul class="MarketingNavigation-module__list__tFbMb"><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_nd_">Platform<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5knd_">AI CODE CREATION</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot" data-analytics-event="{&quot;action&quot;:&quot;github_copilot&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>GitHub Copilot</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Write better code with AI</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/ai/github-app" data-analytics-event="{&quot;action&quot;:&quot;github_copilot_app&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_copilot_app_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mark-github NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656"></path></svg>GitHub Copilot app</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Direct agents from issue to merge</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/mcp" data-analytics-event="{&quot;action&quot;:&quot;mcp_registry&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;mcp_registry_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-mcp NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.52 1.12a3.578 3.578 0 0 1 6.078 2.98 3.578 3.578 0 0 1 2.982 6.08l-3.292 3.293a.252.252 0 0 0 0 .354l.843.843a.749.749 0 1 1-1.06 1.06l-.844-.843a1.75 1.75 0 0 1 0-2.474L13.52 9.12a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0L7.731 9.03A.75.75 0 0 1 6.67 7.97l2.85-2.85a2.08 2.08 0 0 0 0-2.94 2.08 2.08 0 0 0-2.94 0l-4.799 4.8A.75.75 0 0 1 .72 5.92Z"></path><path d="M7.52 3.12a.749.749 0 1 1 1.06 1.06L5.731 7.03A2.079 2.079 0 0 0 8.67 9.97l2.85-2.85a.749.749 0 1 1 1.06 1.06l-2.849 2.85A3.578 3.578 0 0 1 4.67 5.97Z"></path></svg>MCP Registry</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Integrate external tools</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9knd_">DEVELOPER WORKFLOWS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9knd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/actions" data-analytics-event="{&quot;action&quot;:&quot;actions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;actions_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-workflow NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h3.5C6.216 0 7 .784 7 1.75v3.5A1.75 1.75 0 0 1 5.25 7H4v4a1 1 0 0 0 1 1h4v-1.25C9 9.784 9.784 9 10.75 9h3.5c.966 0 1.75.784 1.75 1.75v3.5A1.75 1.75 0 0 1 14.25 16h-3.5A1.75 1.75 0 0 1 9 14.25v-.75H5A2.5 2.5 0 0 1 2.5 11V7h-.75A1.75 1.75 0 0 1 0 5.25Zm1.75-.25a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Zm9 9a.25.25 0 0 0-.25.25v3.5c0 .138.112.25.25.25h3.5a.25.25 0 0 0 .25-.25v-3.5a.25.25 0 0 0-.25-.25Z"></path></svg>Actions</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Automate any workflow</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/codespaces" data-analytics-event="{&quot;action&quot;:&quot;codespaces&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;codespaces_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codespaces NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 11.25c0-.966.784-1.75 1.75-1.75h12.5c.966 0 1.75.784 1.75 1.75v3A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm2-9.5C2 .784 2.784 0 3.75 0h8.5C13.216 0 14 .784 14 1.75v5a1.75 1.75 0 0 1-1.75 1.75h-8.5A1.75 1.75 0 0 1 2 6.75Zm1.75-.25a.25.25 0 0 0-.25.25v5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5a.25.25 0 0 0-.25-.25Zm-2 9.5a.25.25 0 0 0-.25.25v3c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25v-3a.25.25 0 0 0-.25-.25Z"></path><path d="M7 12.75a.75.75 0 0 1 .75-.75h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1-.75-.75Zm-4 0a.75.75 0 0 1 .75-.75h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1-.75-.75Z"></path></svg>Codespaces</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Instant dev environments</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/issues" data-analytics-event="{&quot;action&quot;:&quot;issues&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;issues_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg>Issues</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Plan and track work</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-review" data-analytics-event="{&quot;action&quot;:&quot;code_review&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_review_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg>Code Review</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Manage code changes</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/code-quality" data-analytics-event="{&quot;action&quot;:&quot;code_quality&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_quality_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-codescan-checkmark NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.28 6.28a.75.75 0 1 0-1.06-1.06L6.25 8.19l-.97-.97a.75.75 0 0 0-1.06 1.06l1.5 1.5a.75.75 0 0 0 1.06 0l3.5-3.5Z"></path><path d="M7.5 15a7.5 7.5 0 1 1 5.807-2.754l2.473 2.474a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2.474-2.473A7.472 7.472 0 0 1 7.5 15Zm0-13.5a6 6 0 1 0 4.094 10.386.748.748 0 0 1 .293-.292 6.002 6.002 0 0 0 1.117-6.486A6.002 6.002 0 0 0 7.5 1.5Z"></path></svg>Code Quality</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enforce quality at merge</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dknd_">APPLICATION SECURITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Find and fix vulnerabilities</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/code-security" data-analytics-event="{&quot;action&quot;:&quot;code_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;code_security_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code-square NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25Zm1.75-.25a.25.25 0 0 0-.25.25v12.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25Zm7.47 3.97a.75.75 0 0 1 1.06 0l2 2a.75.75 0 0 1 0 1.06l-2 2a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L10.69 8 9.22 6.53a.75.75 0 0 1 0-1.06ZM6.78 6.53 5.31 8l1.47 1.47a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215l-2-2a.75.75 0 0 1 0-1.06l2-2a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>Code security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Secure your code as you build</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security/secret-protection" data-analytics-event="{&quot;action&quot;:&quot;secret_protection&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;secret_protection_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-lock NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M4 4a4 4 0 0 1 8 0v2h.25c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 12.25 15h-8.5A1.75 1.75 0 0 1 2 13.25v-5.5C2 6.784 2.784 6 3.75 6H4Zm8.25 3.5h-8.5a.25.25 0 0 0-.25.25v5.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25ZM10.5 6V4a2.5 2.5 0 1 0-5 0v2Z"></path></svg>Secret protection</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Stop leaks before they start</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ NavGroup-module__hasSeparator__FnMrN"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_hknd_">EXPLORE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_hknd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/why-github" data-analytics-event="{&quot;action&quot;:&quot;why_github&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;why_github_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Why GitHub</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog" data-analytics-event="{&quot;action&quot;:&quot;blog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;blog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Blog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://github.blog/changelog" data-analytics-event="{&quot;action&quot;:&quot;changelog&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;changelog_link_platform_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Changelog</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/marketplace" data-analytics-event="{&quot;action&quot;:&quot;marketplace&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;marketplace_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Marketplace</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/features" data-analytics-event="{&quot;action&quot;:&quot;view_all_features&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;platform&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_features_link_platform_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all features</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_17d_">Solutions<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_17d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5l7d_">BY COMPANY SIZE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprises&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprises_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Enterprises</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/team" data-analytics-event="{&quot;action&quot;:&quot;small_and_medium_teams&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;small_and_medium_teams_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Small and medium teams</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/enterprise/startups" data-analytics-event="{&quot;action&quot;:&quot;startups&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;startups_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Startups</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/nonprofits" data-analytics-event="{&quot;action&quot;:&quot;nonprofits&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;nonprofits_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Nonprofits</span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9l7d_">BY USE CASE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9l7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/app-modernization" data-analytics-event="{&quot;action&quot;:&quot;app_modernization&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;app_modernization_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">App Modernization</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devsecops" data-analytics-event="{&quot;action&quot;:&quot;devsecops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devsecops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevSecOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/use-case/ci-cd" data-analytics-event="{&quot;action&quot;:&quot;ci/cd&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ci/cd_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">CI/CD</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/use-case" data-analytics-event="{&quot;action&quot;:&quot;view_all_use_cases&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_use_cases_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all use cases</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dl7d_">BY INDUSTRY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dl7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/healthcare" data-analytics-event="{&quot;action&quot;:&quot;healthcare&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;healthcare_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Healthcare</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/financial-services" data-analytics-event="{&quot;action&quot;:&quot;financial_services&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;financial_services_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Financial services</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/manufacturing" data-analytics-event="{&quot;action&quot;:&quot;manufacturing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;manufacturing_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Manufacturing</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/industry/government" data-analytics-event="{&quot;action&quot;:&quot;government&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;government_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Government</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions/industry" data-analytics-event="{&quot;action&quot;:&quot;view_all_industries&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_industries_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all industries</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/solutions" data-analytics-event="{&quot;action&quot;:&quot;view_all_solutions&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;solutions&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_solutions_link_solutions_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all solutions</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_1nd_">Resources<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_1nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5lnd_">EXPLORE BY TOPIC</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=ai" data-analytics-event="{&quot;action&quot;:&quot;ai&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ai_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">AI</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=software-development" data-analytics-event="{&quot;action&quot;:&quot;software_development&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;software_development_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Software Development</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=devops" data-analytics-event="{&quot;action&quot;:&quot;devops&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;devops_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">DevOps</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/articles?topic=security" data-analytics-event="{&quot;action&quot;:&quot;security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources/articles" data-analytics-event="{&quot;action&quot;:&quot;view_all_topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_topics_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all topics</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9lnd_">EXPLORE BY TYPE</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9lnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/customer-stories" data-analytics-event="{&quot;action&quot;:&quot;customer_stories&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_stories_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer stories</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/events" data-analytics-event="{&quot;action&quot;:&quot;events__webinars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;events__webinars_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Events &amp; webinars</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/resources/whitepapers" data-analytics-event="{&quot;action&quot;:&quot;ebooks__reports&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;ebooks__reports_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Ebooks &amp; reports</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/solutions/executive-insights" data-analytics-event="{&quot;action&quot;:&quot;business_insights&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;business_insights_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Business insights</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://skills.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_skills&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_skills_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Skills</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dlnd_">SUPPORT &amp; SERVICES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dlnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://docs.github.com" data-analytics-event="{&quot;action&quot;:&quot;documentation&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;documentation_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Documentation</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://support.github.com" data-analytics-event="{&quot;action&quot;:&quot;customer_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;customer_support_link_resources_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Customer support</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/orgs/community/discussions" data-analytics-event="{&quot;action&quot;:&quot;community_forum&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;community_forum_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Community forum</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trust-center" data-analytics-event="{&quot;action&quot;:&quot;trust_center&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trust_center_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trust center</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/partners" data-analytics-event="{&quot;action&quot;:&quot;partners&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;partners_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Partners</span></a></li></ul></div></li></ul><div class="NavDropdown-module__trailingLinkContainer__VgJGL"><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--arrow-end___esdN8" href="https://github.com/resources" data-analytics-event="{&quot;action&quot;:&quot;view_all_resources&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;resources&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;view_all_resources_link_resources_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">View all resources</span><svg class="Primer_Brand__ExpandableArrow-module__ExpandableArrow___aaZs9 Primer_Brand__Link-module__Link-arrow___yd78i" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><path fill="currentColor" d="M7.28033 3.21967C6.98744 2.92678 6.51256 2.92678 6.21967 3.21967C5.92678 3.51256 5.92678 3.98744 6.21967 4.28033L7.28033 3.21967ZM11 8L11.5303 8.53033C11.8232 8.23744 11.8232 7.76256 11.5303 7.46967L11 8ZM6.21967 11.7197C5.92678 12.0126 5.92678 12.4874 6.21967 12.7803C6.51256 13.0732 6.98744 13.0732 7.28033 12.7803L6.21967 11.7197ZM6.21967 4.28033L10.4697 8.53033L11.5303 7.46967L7.28033 3.21967L6.21967 4.28033ZM10.4697 7.46967L6.21967 11.7197L7.28033 12.7803L11.5303 8.53033L10.4697 7.46967Z"></path><path class="Primer_Brand__ExpandableArrow-module__ExpandableArrow-stem___0K8Hz" stroke="currentColor" d="M1.75 8H11" stroke-width="1.5" stroke-linecap="round"></path></svg></a></div></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_27d_">Open Source<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_27d_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5m7d_">COMMUNITY</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/open-source/sponsors" data-analytics-event="{&quot;action&quot;:&quot;github_sponsors&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_sponsors_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sponsor-tiers NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.586 1C12.268 1 13.5 2.37 13.5 4.25c0 1.745-.996 3.359-2.622 4.831-.166.15-.336.297-.509.438l1.116 5.584a.75.75 0 0 1-.991.852l-2.409-.876a.25.25 0 0 0-.17 0l-2.409.876a.75.75 0 0 1-.991-.852L5.63 9.519a13.78 13.78 0 0 1-.51-.438C3.497 7.609 2.5 5.995 2.5 4.25 2.5 2.37 3.732 1 5.414 1c.963 0 1.843.403 2.474 1.073L8 2.198l.112-.125a3.385 3.385 0 0 1 2.283-1.068L10.586 1Zm-3.621 9.495-.718 3.594 1.155-.42a1.75 1.75 0 0 1 1.028-.051l.168.051 1.154.42-.718-3.592c-.199.13-.37.235-.505.314l-.169.097a.75.75 0 0 1-.72 0 9.54 9.54 0 0 1-.515-.308l-.16-.105ZM10.586 2.5c-.863 0-1.611.58-1.866 1.459-.209.721-1.231.721-1.44 0C7.025 3.08 6.277 2.5 5.414 2.5 4.598 2.5 4 3.165 4 4.25c0 1.23.786 2.504 2.128 3.719.49.443 1.018.846 1.546 1.198l.325.21.076-.047.251-.163a13.341 13.341 0 0 0 1.546-1.198C11.214 6.754 12 5.479 12 4.25c0-1.085-.598-1.75-1.414-1.75Z"></path></svg>GitHub Sponsors</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Fund open source developers</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9m7d_">PROGRAMS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9m7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://securitylab.github.com" data-analytics-event="{&quot;action&quot;:&quot;security_lab&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;security_lab_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Security Lab</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://maintainers.github.com" data-analytics-event="{&quot;action&quot;:&quot;maintainer_community&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;maintainer_community_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Maintainer Community</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://stars.github.com" data-analytics-event="{&quot;action&quot;:&quot;github_stars&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_stars_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">GitHub Stars</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 Primer_Brand__Link-module__Link--is-external___xsncV" href="https://archiveprogram.github.com" data-analytics-event="{&quot;action&quot;:&quot;archive_program&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;archive_program_link_open_source_navbar&quot;}" target="_blank" rel="noreferrer"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Archive Program</span><svg data-component="Octicon" focusable="false" aria-label="External link" class="octicon octicon-link-external" role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.75 2h3.5a.75.75 0 0 1 0 1.5h-3.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25h8.5a.25.25 0 0 0 .25-.25v-3.5a.75.75 0 0 1 1.5 0v3.5A1.75 1.75 0 0 1 12.25 14h-8.5A1.75 1.75 0 0 1 2 12.25v-8.5C2 2.784 2.784 2 3.75 2Zm6.854-1h4.146a.25.25 0 0 1 .25.25v4.146a.25.25 0 0 1-.427.177L13.03 4.03 9.28 7.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.75-3.75-1.543-1.543A.25.25 0 0 1 10.604 1Z"></path></svg></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_dm7d_">REPOSITORIES</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_dm7d_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/topics" data-analytics-event="{&quot;action&quot;:&quot;topics&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;topics_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Topics</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/trending" data-analytics-event="{&quot;action&quot;:&quot;trending&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;trending_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Trending</span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0" href="https://github.com/collections" data-analytics-event="{&quot;action&quot;:&quot;collections&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;open_source&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;collections_link_open_source_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty">Collections</span></a></li></ul></div></li></ul></div></div></li><li><div class="NavDropdown-module__container__l2YeI"><button type="button" class="NavDropdown-module__button__PEHWX" aria-expanded="false" aria-controls="_R_2nd_">Enterprise<svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-right NavDropdown-module__buttonIcon__Tkl8_" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"></path></svg></button><div id="_R_2nd_" class="NavDropdown-module__dropdown__xm1jd"><ul class="NavDropdown-module__list__zuCgG"><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_5mnd_">ENTERPRISE SOLUTIONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_5mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise" data-analytics-event="{&quot;action&quot;:&quot;enterprise_platform&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;enterprise_platform_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-stack NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.122.392a1.75 1.75 0 0 1 1.756 0l5.003 2.902c.83.481.83 1.68 0 2.162L8.878 8.358a1.75 1.75 0 0 1-1.756 0L2.119 5.456a1.251 1.251 0 0 1 0-2.162ZM8.125 1.69a.248.248 0 0 0-.25 0l-4.63 2.685 4.63 2.685a.248.248 0 0 0 .25 0l4.63-2.685ZM1.601 7.789a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0L1.874 8.814A.75.75 0 0 1 1.6 7.789Zm0 3.5a.75.75 0 0 1 1.025-.273l5.249 3.044a.248.248 0 0 0 .25 0l5.249-3.044a.75.75 0 0 1 .752 1.298l-5.248 3.044a1.75 1.75 0 0 1-1.756 0l-5.248-3.044a.75.75 0 0 1-.273-1.025Z"></path></svg>Enterprise platform</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">AI-powered developer platform</span></span></a></li></ul></div></li><li><div class="NavGroup-module__group__W8SqJ"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--monospace___QXHDQ Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavGroup-module__title__Wzxz2" id="_R_9mnd_">AVAILABLE ADD-ONS</span><ul class="NavGroup-module__list__UCOFy" aria-labelledby="_R_9mnd_"><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/security/advanced-security" data-analytics-event="{&quot;action&quot;:&quot;github_advanced_security&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;github_advanced_security_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield-check NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m8.533.133 5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667l5.25-1.68a1.748 1.748 0 0 1 1.066 0Zm-.61 1.429.001.001-5.25 1.68a.251.251 0 0 0-.174.237V7c0 1.36.275 2.666 1.057 3.859.784 1.194 2.121 2.342 4.366 3.298a.196.196 0 0 0 .154 0c2.245-.957 3.582-2.103 4.366-3.297C13.225 9.666 13.5 8.358 13.5 7V3.48a.25.25 0 0 0-.174-.238l-5.25-1.68a.25.25 0 0 0-.153 0ZM11.28 6.28l-3.5 3.5a.75.75 0 0 1-1.06 0l-1.5-1.5a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l.97.97 2.97-2.97a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg>GitHub Advanced Security</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade security features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/features/copilot/copilot-business" data-analytics-event="{&quot;action&quot;:&quot;copilot_for_business&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;copilot_for_business_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copilot NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.998 15.035c-4.562 0-7.873-2.914-7.998-3.749V9.338c.085-.628.677-1.686 1.588-2.065.013-.07.024-.143.036-.218.029-.183.06-.384.126-.612-.201-.508-.254-1.084-.254-1.656 0-.87.128-1.769.693-2.484.579-.733 1.494-1.124 2.724-1.261 1.206-.134 2.262.034 2.944.765.05.053.096.108.139.165.044-.057.094-.112.143-.165.682-.731 1.738-.899 2.944-.765 1.23.137 2.145.528 2.724 1.261.566.715.693 1.614.693 2.484 0 .572-.053 1.148-.254 1.656.066.228.098.429.126.612.012.076.024.148.037.218.924.385 1.522 1.471 1.591 2.095v1.872c0 .766-3.351 3.795-8.002 3.795Zm0-1.485c2.28 0 4.584-1.11 5.002-1.433V7.862l-.023-.116c-.49.21-1.075.291-1.727.291-1.146 0-2.059-.327-2.71-.991A3.222 3.222 0 0 1 8 6.303a3.24 3.24 0 0 1-.544.743c-.65.664-1.563.991-2.71.991-.652 0-1.236-.081-1.727-.291l-.023.116v4.255c.419.323 2.722 1.433 5.002 1.433ZM6.762 2.83c-.193-.206-.637-.413-1.682-.297-1.019.113-1.479.404-1.713.7-.247.312-.369.789-.369 1.554 0 .793.129 1.171.308 1.371.162.181.519.379 1.442.379.853 0 1.339-.235 1.638-.54.315-.322.527-.827.617-1.553.117-.935-.037-1.395-.241-1.614Zm4.155-.297c-1.044-.116-1.488.091-1.681.297-.204.219-.359.679-.242 1.614.091.726.303 1.231.618 1.553.299.305.784.54 1.638.54.922 0 1.28-.198 1.442-.379.179-.2.308-.578.308-1.371 0-.765-.123-1.242-.37-1.554-.233-.296-.693-.587-1.713-.7Z"></path><path d="M6.25 9.037a.75.75 0 0 1 .75.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 .75-.75Zm4.25.75v1.501a.75.75 0 0 1-1.5 0V9.787a.75.75 0 0 1 1.5 0Z"></path></svg>Copilot for Business</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade AI features</span></span></a></li><li><a class="Primer_Brand__Link-module__Link___lF11y Primer_Brand__Link-module__Link--default___VRVW0 NavLink-module__link__EG3d4" href="https://github.com/enterprise/premium-support" data-analytics-event="{&quot;action&quot;:&quot;premium_support&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;enterprise&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;premium_support_link_enterprise_navbar&quot;}"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Link-module__Link--label___jM8Ty"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS Primer_Brand__Text-module__Text--weight-medium___qJKf_ NavLink-module__title__Q7t0p"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-comment-discussion NavLink-module__icon__ltGNM" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.75 1h8.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 10.25 10H7.061l-2.574 2.573A1.458 1.458 0 0 1 2 11.543V10h-.25A1.75 1.75 0 0 1 0 8.25v-5.5C0 1.784.784 1 1.75 1ZM1.5 2.75v5.5c0 .138.112.25.25.25h1a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h3.5a.25.25 0 0 0 .25-.25v-5.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25Zm13 2a.25.25 0 0 0-.25-.25h-.5a.75.75 0 0 1 0-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0 1 14.25 12H14v1.543a1.458 1.458 0 0 1-2.487 1.03L9.22 12.28a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215l2.22 2.22v-2.19a.75.75 0 0 1 .75-.75h1a.25.25 0 0 0 .25-.25Z"></path></svg>Premium Support</span><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--muted___rE6mh Primer_Brand__Text-module__Text--200____P1wy Primer_Brand__Text-module__Text--antialiased___TYoXS NavLink-module__subtitle__X4gkW">Enterprise-grade 24/7 support</span></span></a></li></ul></div></li></ul></div></div></li><li><a href="https://github.com/pricing" data-analytics-event="{&quot;action&quot;:&quot;pricing&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;pricing&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;pricing_link_pricing_navbar&quot;}" class="MarketingNavigation-module__navLink__hUomM">Pricing</a></li></ul></nav><div class="MarketingHeader-module__ctaContainer__tBmPz"><div class="HeaderSearch-module__searchSlot__oVOUS"><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderSearch-module__trigger__zsF9q" type="button" aria-haspopup="dialog" aria-expanded="false" aria-label="Search or jump to, type / to search" data-analytics-event="{&quot;action&quot;:&quot;searchbar&quot;,&quot;tag&quot;:&quot;input&quot;,&quot;context&quot;:&quot;global&quot;,&quot;location&quot;:&quot;navbar&quot;,&quot;label&quot;:&quot;searchbar_input_global_navbar&quot;}"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"><span class="HeaderSearch-module__content__kMpxU"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search HeaderSearch-module__icon__wcrHX" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg><span class="HeaderSearch-module__label__d1iWG">Search</span><kbd class="HeaderSearch-module__kbd__HNG0o" aria-hidden="true">/</kbd></span></span></span></button><div class="d-none"></div></div><div class="AuthCTAs-module__signInWrap__q2P60"><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq AuthCTAs-module__desktopActionGap__UZuXT AuthCTAs-module__hiddenBelowLg__BfKBw" href="/login?return_to=https%3A%2F%2Fgithub.com%2Fsavushkin-dev%2Fscada-mobile%2Fblob%2Fmain%2FFRONTEND_DATA_SOURCES.md" data-analytics-event="{&quot;action&quot;:&quot;sign_in&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_in_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="41c8f37a7cbd88937435d6f7244f956911e76e3b4b106d521f46333982758c51"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH">Sign in</span></span></a></div><a class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--secondary___gHnw_ Primer_Brand__Button-module__Button--size-small___zQrEw AuthCTAs-module__cta__WpwQq" href="/signup?ref_cta=Sign+up&amp;ref_loc=header+logged+out&amp;ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&amp;source=header-repo&amp;source_repo=savushkin-dev%2Fscada-mobile" data-analytics-event="{&quot;action&quot;:&quot;sign_up&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;context&quot;:&quot;auth_cta&quot;,&quot;location&quot;:&quot;header&quot;,&quot;label&quot;:&quot;sign_up_link_auth_cta_header&quot;}" data-hydro-click="{&quot;event_type&quot;:&quot;authentication.click&quot;,&quot;payload&quot;:{&quot;location_in_page&quot;:&quot;site header menu&quot;,&quot;repository_id&quot;:null,&quot;auth_type&quot;:&quot;SIGN_UP&quot;,&quot;originating_url&quot;:&quot;https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md&quot;,&quot;user_id&quot;:null}}" data-hydro-click-hmac="41c8f37a7cbd88937435d6f7244f956911e76e3b4b106d521f46333982758c51"><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-secondary___eJ0_a">Sign up</span></span></a><button class="Primer_Brand__Button-module__Button___scH9Z Primer_Brand__Button-module__Button--subtle___F7pEE Primer_Brand__Button-module__Button--size-small___zQrEw HeaderAppearanceSettings-module__trigger__hUheK" type="button" aria-haspopup="dialog" aria-labelledby="_R_fbd_"><span class="Primer_Brand__Button-module__Button__leading-visual___jjtTe" data-testid="Button-leading-visual"><svg data-component="Octicon" focusable="false" aria-hidden="true" class="octicon octicon-sliders Primer_Brand__Button-module__Button__icon-visual____qybb" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M15 2.75a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h4a.75.75 0 0 1 .75.75Zm-8.5.75v1.25a.75.75 0 0 0 1.5 0v-4a.75.75 0 0 0-1.5 0V2H1.75a.75.75 0 0 0 0 1.5H6.5Zm1.25 5.25a.75.75 0 0 0 0-1.5h-6a.75.75 0 0 0 0 1.5h6ZM15 8a.75.75 0 0 1-.75.75H11.5V10a.75.75 0 1 1-1.5 0V6a.75.75 0 0 1 1.5 0v1.25h2.75A.75.75 0 0 1 15 8Zm-9 5.25v-2a.75.75 0 0 0-1.5 0v1.25H1.75a.75.75 0 0 0 0 1.5H4.5v1.25a.75.75 0 0 0 1.5 0v-2Zm9 0a.75.75 0 0 1-.75.75h-6a.75.75 0 0 1 0-1.5h6a.75.75 0 0 1 .75.75Z"></path></svg></span><span class="Primer_Brand__Button-module__Button__text___ED0bX"><span class="Primer_Brand__Text-module__Text___XeGJJ Primer_Brand__Text-module__Text-font--mona-sans___a8XJD Primer_Brand__Text-module__Text--default___GhPh_ Primer_Brand__Text-module__Text--100___B2ueX Primer_Brand__Text-module__Text--weight-medium___qJKf_ Primer_Brand__Button-module__Button--label___qrkyz Primer_Brand__Button-module__Button--label-subtle___8ndWH"></span></span></button><div class="Primer_Brand__Tooltip-module__Tooltip___0Eipx" data-direction="s" aria-hidden="true" id="_R_fbd_">Appearance settings</div></div></div></div></div><div class="MarketingHeader-module__bottomBorder__uZT38" aria-hidden="true"></div></header></div></div>
</react-partial>



      <div hidden="hidden" data-view-component="true" class="js-stale-session-flash stale-session-flash flash flash-warn flash-full">
  
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
        <span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>
        <span class="js-stale-session-flash-switched" hidden>You switched accounts on another tab or window. <a class="Link--inTextBlock" href="">Reload</a> to refresh your session.</span>

    <button id="icon-button-84ec344d-5560-4db0-a2cc-166c52291be6" aria-labelledby="tooltip-e981f5b6-16da-4aaa-868a-8a70e829e835" type="button" data-view-component="true" class="Button Button--iconOnly Button--invisible Button--medium flash-close js-flash-close">  <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x Button-visual">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
</button><tool-tip id="tooltip-e981f5b6-16da-4aaa-868a-8a70e829e835" for="icon-button-84ec344d-5560-4db0-a2cc-166c52291be6" popover="manual" data-direction="s" data-type="label" data-view-component="true" class="sr-only position-absolute">Dismiss alert</tool-tip>


  
</div>
    </div>

  <div id="start-of-content" class="show-on-focus"></div>








    <div id="js-flash-container" class="flash-container" data-turbo-replace>






  <template class="js-flash-template">
    
<div class="flash flash-full   {{ className }}">
  <div >
    <button autofocus class="flash-close js-flash-close" type="button" aria-label="Dismiss this message">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    <div aria-atomic="true" role="alert" class="js-flash-alert">
      
      <div>{{ message }}</div>

    </div>
  </div>
</div>
  </template>
</div>


    






  <div
    class="application-main "
    data-commit-hovercards-enabled
    data-discussion-hovercards-enabled
    data-issue-and-pr-hovercards-enabled
    data-project-hovercards-enabled
  >
        <div itemscope itemtype="http://schema.org/SoftwareSourceCode" class="">
    <main id="js-repo-pjax-container" >
      
      
    


    








  

    <link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ym-6bc2c9bf8112c038.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/sk2-8deeef9351be3911.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/ni-3e194d222c7770bf.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/7x-71d30cb240cd901f.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wwk-ad869086822937b1.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a1-9f79841ecac1da8e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/wzg-40a3213b3e83400b.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/5f-64f8e7d5bcbd4787.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/z5-b53a93078d026f1e.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/8h-cef26f68c1331011.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/fq-3ce13a77e863807a.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/a7-596b507bd20acddc.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/xfx-c82d8352413aea0a.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/j4-b3a9e87fa7878a98.js" fetchpriority="low" />
<link crossorigin="anonymous" rel="modulepreload" href="https://github.githubassets.com/assets/global-nav-bar-29c7aef68ee9db41.js" fetchpriority="low" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react-css.2d881b4d4a503d1c.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/pj.75627bfeb9ceb1da.module.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-nav-bar.2f2560cba06ab359.module.css" />

<react-partial
  partial-name="global-nav-bar"
  data-ssr="true"
  data-attempted-ssr="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-partial.embeddedData">{"props":{"contextRegion":{"crumbs":[{"crumb_type":"organization","label":"savushkin-dev","is_root":false,"href":"/savushkin-dev"},{"crumb_type":"repository","label":"scada-mobile","is_root":false,"href":"/savushkin-dev/scada-mobile"}],"localNavigation":[{"id":"code","icon":"code","label":"Code","href":"/savushkin-dev/scada-mobile","selectedLinks":["repo_source","repo_downloads","repo_commits","repo_releases","repo_tags","repo_branches","repo_packages","repo_deployments","repo_attestations"],"popoverTarget":false,"commandId":"repositories:go-to-code","reactNav":{"appTarget":"code-view","anchor":"code-view-repo-link"},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"issues","icon":"issue-opened","label":"Issues","href":"/savushkin-dev/scada-mobile/issues","selectedLinks":["repo_issues","repo_labels","repo_milestones"],"count":15,"popoverTarget":false,"commandId":"repositories:go-to-issues","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"pull-requests","icon":"git-pull-request","label":"Pull requests","href":"/savushkin-dev/scada-mobile/pulls","selectedLinks":["repo_pulls","checks"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-pull-requests","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"actions","icon":"play","label":"Actions","href":"/savushkin-dev/scada-mobile/actions","selectedLinks":["repo_actions"],"popoverTarget":false,"commandId":"repositories:go-to-actions","reactNav":{"appTarget":"actions-workflows","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"projects","icon":"table","label":"Projects","href":"/savushkin-dev/scada-mobile/projects","selectedLinks":["repo_projects","new_repo_project","repo_project"],"popoverTarget":false,"commandId":"repositories:go-to-projects","reactNav":{"appTarget":"repo","anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"security-and-quality","icon":"shield","label":"Security and quality","href":"/savushkin-dev/scada-mobile/security","selectedLinks":["security","overview","alerts","policy","token_scanning","code_scanning"],"count":0,"popoverTarget":false,"commandId":"repositories:go-to-security","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}},{"id":"insights","icon":"graph","label":"Insights","href":"/savushkin-dev/scada-mobile/pulse","selectedLinks":["repo_graphs","repo_contributors","dependency_graph","dependabot_updates","pulse","people","community"],"popoverTarget":false,"commandId":"repositories:go-to-insights","reactNav":{"appTarget":null,"anchor":null},"turboNav":{"frame":"repo-content-turbo-frame"}}],"localNavigationUpdateChannel":null,"selectedLink":"repo_source"},"owner":null,"headerLogo":{"href":"/","aria-label":"Homepage "},"notifications":{"indicatorMode":"disabled","websocketChannel":null,"fetchIndicatorSrc":"/notifications/indicator","fetchIndicatorEnabled":false},"issues":{"href":"/issues"},"pulls":{"href":"/pulls"},"contributedRepos":{"href":"/repos"},"copilot":{"show":false,"showAgentsButton":false,"showRelaunchAnnouncement":false,"copilotChatUrl":null,"copilotApiUrl":"https://api.githubcopilot.com"},"search":{"show":true,"showCommandPalette":false,"isSearchPage":false,"isJumpToSearch":false,"searchContext":{"scope":"repo:savushkin-dev/scada-mobile","current_repo_name":"scada-mobile","current_repo_nwo":"savushkin-dev/scada-mobile","current_repo_org":"savushkin-dev","user_id":"savushkin-dev"}},"commandPalette":null,"enterpriseBar":{"show":false},"globalTransactionalMessage":[],"payloadsUrl":"/_global-navigation/payloads.json?can_toggle_site_admin_and_employee_status=0\u0026is_admin_mode_on=0\u0026is_ui_opted_out=0\u0026show_ui_opt_out=0\u0026v=7","contextRegionUrl":"/_global-navigation/context-region.json"}}</script>
  <div data-target="react-partial.reactRoot"><header aria-label="Global navigation menu" data-component="Stack" class="GlobalNav styles-module__appHeader__YzYWk prc-Stack-Stack-UQ9k6" data-gap="none" data-direction="vertical" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="none"><div data-component="Stack" class="prc-Stack-Stack-UQ9k6" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="center" data-padding="none"><div data-testid="top-nav-center" data-component="Stack" class="styles-module__center__R3QRv styles-module__withLocalNavigation__rjTJ_ GlobalNavBar-module__loggedOut__Jv1rk prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="stretch" data-wrap="nowrap" data-justify="start" data-padding="normal"><nav class="styles-module__contextRegion__VbSp2 prc-Breadcrumbs-BreadcrumbsBase-3Gb-B" aria-label="Breadcrumbs" data-overflow="menu" data-variant="normal" data-component="Breadcrumbs"><ol class="prc-Breadcrumbs-BreadcrumbsList-BKjpe"><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/savushkin-dev" data-discover="true"><span class="">savushkin-dev</span></a></li><li class="prc-Breadcrumbs-ItemWrapper-k0NLn"><a class="styles-module__contextCrumb__IzGIq prc-Breadcrumbs-Item-jcraJ" data-component="Breadcrumbs.Item" href="/savushkin-dev/scada-mobile" data-discover="true"><span class="styles-module__contextCrumbLast__tI2e3">scada-mobile</span></a></li></ol></nav></div><div data-testid="top-nav-right" data-component="Stack" class="styles-module__right__mlBQg styles-module__withLocalNavigation__rjTJ_ styles-module__rightWithResponsiveCreateButton__SKn2W prc-Stack-Stack-UQ9k6" data-gap="condensed" data-direction="horizontal" data-align="center" data-wrap="nowrap" data-justify="start" data-padding="normal"></div></div><h2 class="prc-src-InternalVisuallyHidden-2YaI6">Repository navigation</h2><nav class="prc-components-UnderlineWrapper-eT-Yj prc-UnderlineNav-UnderlineWrapper-GWONT LocalNavigation-module__LocalNavigation__b0Xc0" aria-label="Repository" data-variant="inset" data-overflow-mode="wrap" data-hide-icons-breakpoint="medium"><ul class="prc-UnderlineNav-ItemsList-oj8gN prc-components-UnderlineItemList-xKlKC" role="list"><li role="presentation" aria-hidden="true" class="prc-UnderlineNav-WrapSpacer--aLgz"></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a aria-current="page" data-tab-item="code" data-react-nav="code-view" data-react-nav-anchor="code-view-repo-link" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-code" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m11.28 3.22 4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.749.749 0 0 1-1.275-.326.749.749 0 0 1 .215-.734L13.94 8l-3.72-3.72a.749.749 0 0 1 .326-1.275.749.749 0 0 1 .734.215Zm-6.56 0a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L2.06 8l3.72 3.72a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L.47 8.53a.75.75 0 0 1 0-1.06Z"></path></svg></span><span data-component="text" data-content="Code">Code</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="issues" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/issues" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-issue-opened" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Z"></path></svg></span><span data-component="text" data-content="Issues">Issues</span><span data-component="counter"><span aria-hidden="true" data-variant="secondary" data-component="CounterLabel" class="prc-CounterLabel-CounterLabel-X-kRU">15</span><span class="prc-VisuallyHidden-VisuallyHidden-Q0qSB"> (<!-- -->15<!-- -->)</span></span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="pull-requests" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/pulls" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-pull-request" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 3.25a2.25 2.25 0 1 1 3 2.122v5.256a2.251 2.251 0 1 1-1.5 0V5.372A2.25 2.25 0 0 1 1.5 3.25Zm5.677-.177L9.573.677A.25.25 0 0 1 10 .854V2.5h1A2.5 2.5 0 0 1 13.5 5v5.628a2.251 2.251 0 1 1-1.5 0V5a1 1 0 0 0-1-1h-1v1.646a.25.25 0 0 1-.427.177L7.177 3.427a.25.25 0 0 1 0-.354ZM3.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm0 9.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Zm8.25.75a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Z"></path></svg></span><span data-component="text" data-content="Pull requests">Pull requests</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="actions" data-react-nav="actions-workflows" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/actions" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-play" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 0a8 8 0 1 1 0 16A8 8 0 0 1 8 0ZM1.5 8a6.5 6.5 0 1 0 13 0 6.5 6.5 0 0 0-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"></path></svg></span><span data-component="text" data-content="Actions">Actions</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="projects" data-react-nav="repo" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/projects" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-table" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25ZM6.5 6.5v8h7.75a.25.25 0 0 0 .25-.25V6.5Zm8-1.5V1.75a.25.25 0 0 0-.25-.25H6.5V5Zm-13 1.5v7.75c0 .138.112.25.25.25H5v-8ZM5 5V1.5H1.75a.25.25 0 0 0-.25.25V5Z"></path></svg></span><span data-component="text" data-content="Projects">Projects</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="security-and-quality" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/security" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-shield" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.467.133a1.748 1.748 0 0 1 1.066 0l5.25 1.68A1.75 1.75 0 0 1 15 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.697 1.697 0 0 1-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 0 1 1.217-1.667Zm.61 1.429a.25.25 0 0 0-.153 0l-5.25 1.68a.25.25 0 0 0-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.196.196 0 0 0 .154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.251.251 0 0 0-.174-.237l-5.25-1.68ZM8.75 4.75v3a.75.75 0 0 1-1.5 0v-3a.75.75 0 0 1 1.5 0ZM9 10.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path></svg></span><span data-component="text" data-content="Security and quality">Security and quality</span></a></li><li class="prc-UnderlineNav-UnderlineNavItem-syRjR"><a data-tab-item="insights" data-turbo-frame="repo-content-turbo-frame" class="prc-components-UnderlineItem-7fP-n" href="/savushkin-dev/scada-mobile/pulse" data-discover="true"><span data-component="icon"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-graph" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M1.5 1.75V13.5h13.75a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1-.75-.75V1.75a.75.75 0 0 1 1.5 0Zm14.28 2.53-5.25 5.25a.75.75 0 0 1-1.06 0L7 7.06 4.28 9.78a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042l3.25-3.25a.75.75 0 0 1 1.06 0L10 7.94l4.72-4.72a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042Z"></path></svg></span><span data-component="text" data-content="Insights">Insights</span></a></li></ul><div class="prc-UnderlineNav-MoreButtonContainer-Dnrq6"><div class="prc-UnderlineNav-MoreButtonDivider-dN0a-"></div><button data-component="overflow-menu-button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk prc-UnderlineNav-MoreButton-Y8soj" data-loading="false" data-size="medium" data-variant="invisible" id="_R_2ktp_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3"><span>More<span class="prc-src-InternalVisuallyHidden-2YaI6"> items</span></span></span></span><span data-component="trailingAction" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></button></div></nav><div class="d-none"></div></header></div>
</react-partial>


  



<turbo-frame id="repo-content-turbo-frame" target="_top" data-turbo-action="advance" class="">
    <div id="repo-content-pjax-container" class="repository-content " >
    



    
      
    








<react-app
  app-name="code-view"
  initial-path="/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md"
  style="display: block; min-height: calc(100vh - 64px);"
  data-attempted-ssr="true"
  data-ssr="true"
  data-lazy="false"
  data-alternate="false"
  data-data-router-enabled="true"
  data-react-profiling="false"
>
  
  <script type="application/json" data-target="react-app.embeddedData">{"payload":{"codeViewBlobRoute":{"csv":null,"csvError":null,"headerInfo":{"toc":[{"level":1,"text":"Источники данных для фронтенда","anchor":"источники-данных-для-фронтенда","htmlText":"Источники данных для фронтенда"},{"level":2,"text":"Purpose","anchor":"purpose","htmlText":"Purpose"},{"level":2,"text":"Table of contents","anchor":"table-of-contents","htmlText":"Table of contents"},{"level":2,"text":"Что это за документ","anchor":"что-это-за-документ","htmlText":"Что это за документ"},{"level":2,"text":"Важная оговорка по области применимости","anchor":"важная-оговорка-по-области-применимости","htmlText":"Важная оговорка по области применимости"},{"level":2,"text":"Главный вывод","anchor":"главный-вывод","htmlText":"Главный вывод"},{"level":2,"text":"Подтвержденные backend-источники для Grunw11","anchor":"подтвержденные-backend-источники-для-grunw11","htmlText":"Подтвержденные backend-источники для Grunw11"},{"level":3,"text":"Реальные unit-источники","anchor":"реальные-unit-источники","htmlText":"Реальные unit-источники"},{"level":3,"text":"Что делает scada","anchor":"что-делает-scada","htmlText":"Что делает scada"},{"level":3,"text":"Маппинг устройств в SCADA-диагностику","anchor":"маппинг-устройств-в-scada-диагностику","htmlText":"Маппинг устройств в SCADA-диагностику"},{"level":2,"text":"1. REST topology","anchor":"1-rest-topology","htmlText":"1. REST topology"},{"level":3,"text":"GET /api/v1.0.0/workshops/topology","anchor":"get-apiv100workshopstopology","htmlText":"GET /api/v1.0.0/workshops/topology"},{"level":3,"text":"GET /api/v1.0.0/workshops/{workshopId}/units/topology","anchor":"get-apiv100workshopsworkshopidunitstopology","htmlText":"GET /api/v1.0.0/workshops/{workshopId}/units/topology"},{"level":3,"text":"GET /api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology","anchor":"get-apiv100workshopsworkshopidunitsunitiddevicestopology","htmlText":"GET /api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology"},{"level":2,"text":"2. WebSocket /ws/unit/{unitId}","anchor":"2-websocket-wsunitunitid","htmlText":"2. WebSocket /ws/unit/{unitId}"},{"level":2,"text":"LINE_STATUS","anchor":"line_status","htmlText":"LINE_STATUS"},{"level":2,"text":"DEVICES_STATUS","anchor":"devices_status","htmlText":"DEVICES_STATUS"},{"level":3,"text":"Принтеры","anchor":"принтеры","htmlText":"Принтеры"},{"level":3,"text":"Камеры агрегации на потоке","anchor":"камеры-агрегации-на-потоке","htmlText":"Камеры агрегации на потоке"},{"level":3,"text":"Камеры агрегации на коробе","anchor":"камеры-агрегации-на-коробе","htmlText":"Камеры агрегации на коробе"},{"level":3,"text":"Checker-камеры","anchor":"checker-камеры","htmlText":"Checker-камеры"},{"level":4,"text":"EAN-checker камеры","anchor":"ean-checker-камеры","htmlText":"EAN-checker камеры"},{"level":4,"text":"CamChecker1 и CamChecker2","anchor":"camchecker1-и-camchecker2","htmlText":"CamChecker1 и CamChecker2"},{"level":2,"text":"QUEUE","anchor":"queue","htmlText":"QUEUE"},{"level":2,"text":"ERRORS","anchor":"errors","htmlText":"ERRORS"},{"level":3,"text":"deviceErrors[]","anchor":"deviceerrors","htmlText":"deviceErrors[]"},{"level":3,"text":"logs[]","anchor":"logs","htmlText":"logs[]"},{"level":2,"text":"3. WebSocket /ws/live","anchor":"3-websocket-wslive","htmlText":"3. WebSocket /ws/live"},{"level":2,"text":"UNITS_STATUS","anchor":"units_status","htmlText":"UNITS_STATUS"},{"level":3,"text":"payload[].event","anchor":"payloadevent","htmlText":"payload[].event"},{"level":3,"text":"payload[].timer","anchor":"payloadtimer","htmlText":"payload[].timer"},{"level":2,"text":"ALERT_SNAPSHOT и ALERT","anchor":"alert_snapshot-и-alert","htmlText":"ALERT_SNAPSHOT и ALERT"},{"level":3,"text":"Какие поля можно брать","anchor":"какие-поля-можно-брать","htmlText":"Какие поля можно брать"},{"level":3,"text":"Из чего собирать сами алерты","anchor":"из-чего-собирать-сами-алерты","htmlText":"Из чего собирать сами алерты"},{"level":3,"text":"errors[].device","anchor":"errorsdevice","htmlText":"errors[].device"},{"level":3,"text":"errors[].code","anchor":"errorscode","htmlText":"errors[].code"},{"level":3,"text":"errors[].message","anchor":"errorsmessage","htmlText":"errors[].message"},{"level":3,"text":"severity","anchor":"severity","htmlText":"severity"},{"level":2,"text":"4. Что можно брать уже сейчас без спорных мест","anchor":"4-что-можно-брать-уже-сейчас-без-спорных-мест","htmlText":"4. Что можно брать уже сейчас без спорных мест"},{"level":3,"text":"Можно забирать сразу","anchor":"можно-забирать-сразу","htmlText":"Можно забирать сразу"},{"level":3,"text":"Требуют отдельного согласования","anchor":"требуют-отдельного-согласования","htmlText":"Требуют отдельного согласования"},{"level":2,"text":"Итоговая рекомендация для backend","anchor":"итоговая-рекомендация-для-backend","htmlText":"Итоговая рекомендация для backend"},{"level":2,"text":"Короткий практический вывод","anchor":"короткий-практический-вывод","htmlText":"Короткий практический вывод"}]},"issueTemplate":null,"discussionTemplate":null,"richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch1 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eИсточники данных для фронтенда\u003c/h1\u003e\u003ca id=\"user-content-источники-данных-для-фронтенда\" class=\"anchor\" aria-label=\"Permalink: Источники данных для фронтенда\" href=\"#источники-данных-для-фронтенда\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePurpose\u003c/h2\u003e\u003ca id=\"user-content-purpose\" class=\"anchor\" aria-label=\"Permalink: Purpose\" href=\"#purpose\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eФиксация подтвержденных источников данных для фронтенд-контракта и правил агрегации.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTable of contents\u003c/h2\u003e\u003ca id=\"user-content-table-of-contents\" class=\"anchor\" aria-label=\"Permalink: Table of contents\" href=\"#table-of-contents\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#purpose\"\u003ePurpose\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%D1%87%D1%82%D0%BE-%D1%8D%D1%82%D0%BE-%D0%B7%D0%B0-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82\"\u003eЧто это за документ\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%D0%B2%D0%B0%D0%B6%D0%BD%D0%B0%D1%8F-%D0%BE%D0%B3%D0%BE%D0%B2%D0%BE%D1%80%D0%BA%D0%B0-%D0%BF%D0%BE-%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8\"\u003eВажная оговорка по области применимости\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D1%8B%D0%B9-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4\"\u003eГлавный вывод\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#%D0%BF%D0%BE%D0%B4%D1%82%D0%B2%D0%B5%D1%80%D0%B6%D0%B4%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-backend-%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%B8-%D0%B4%D0%BB%D1%8F-grunw11\"\u003eПодтвержденные backend-источники для Grunw11\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#1-rest-topology\"\u003e1. REST topology\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eЧто это за документ\u003c/h2\u003e\u003ca id=\"user-content-что-это-за-документ\" class=\"anchor\" aria-label=\"Permalink: Что это за документ\" href=\"#что-это-за-документ\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДокумент фиксирует, откуда именно backend может брать данные для контракта из \u003ca href=\"/savushkin-dev/scada-mobile/blob/main/API_REFERENCE.md\"\u003eAPI_REFERENCE.md\u003c/a\u003e, если ориентироваться на подтвержденные источники PrintSrv и на карту соответствий из внешнего артефакта \u003ccode\u003eGRUNW11_SCADA_OBJECT_MAPPING.md\u003c/code\u003e (файл не хранится в этом репозитории).\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eТекущая база анализа:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eавтомат \u003ccode\u003eGrunw11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003efrontend-контракт из \u003ca href=\"/savushkin-dev/scada-mobile/blob/main/API_REFERENCE.md\"\u003eAPI_REFERENCE.md\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eкарта SCADA/PrintSrv-источников из внешнего артефакта \u003ccode\u003eGRUNW11_SCADA_OBJECT_MAPPING.md\u003c/code\u003e (вне текущего репозитория)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eДокумент отвечает на вопрос: какие поля фронта можно брать напрямую, какие нужно агрегировать, а какие пока не подтверждены по данным Grunw11 и требуют отдельного решения в backend.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eВажная оговорка по области применимости\u003c/h2\u003e\u003ca id=\"user-content-важная-оговорка-по-области-применимости\" class=\"anchor\" aria-label=\"Permalink: Важная оговорка по области применимости\" href=\"#важная-оговорка-по-области-применимости\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЭтот документ нельзя читать как универсальную схему для всех автоматов.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eОн построен на детальном анализе именно \u003ccode\u003eGrunw11\u003c/code\u003e, а \u003ccode\u003eGrunw11\u003c/code\u003e в составе устройств заметно богаче многих других автоматов. По реестру автоматов уже видно, что набор устройств в проекте отличается от линии к линии:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eесть автоматы с одним принтером, например \u003ccode\u003eHassia1\u003c/code\u003e, \u003ccode\u003eHassia2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eесть автоматы с двумя принтерами, например \u003ccode\u003eBosch\u003c/code\u003e, \u003ccode\u003eGrunw1\u003c/code\u003e, \u003ccode\u003eHassia3\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eесть автоматы с четырьмя принтерами и расширенным набором камер, как \u003ccode\u003eGrunw11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eесть автоматы с другим типом камер вообще, например \u003ccode\u003etrepko1\u003c/code\u003e, \u003ccode\u003etrepko2\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eИз этого следует главное правило интеграции:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eколичество камер и принтеров зависит от автомата\u003c/li\u003e\n\u003cli\u003eсписок unit-объектов в PrintSrv зависит от автомата\u003c/li\u003e\n\u003cli\u003eнабор агрегированных полей в \u003ccode\u003escada\u003c/code\u003e тоже зависит от автомата\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eНапример, если на линии нет второй камеры агрегации, то backend не должен ожидать наличие \u003ccode\u003eCamAgregation2\u003c/code\u003e, \u003ccode\u003eDev043...\u003c/code\u003e, \u003ccode\u003eDev044...\u003c/code\u003e и связанных с ними полей вроде \u003ccode\u003eDev043CounterGeneral\u003c/code\u003e или \u003ccode\u003eDev044Error\u003c/code\u003e. Аналогично, если на автомате только один принтер, то не будет ни \u003ccode\u003ePrinter12\u003c/code\u003e, ни \u003ccode\u003eLineDev012...\u003c/code\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eПоэтому все примеры ниже надо читать так:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eдля \u003ccode\u003eGrunw11\u003c/code\u003e подтверждено именно в таком виде\u003c/li\u003e\n\u003cli\u003eдля других автоматов backend обязан сначала определить фактический состав устройств, а уже потом строить topology, \u003ccode\u003eDEVICES_STATUS\u003c/code\u003e, \u003ccode\u003eALERT\u003c/code\u003e и другие ответы\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eГлавный вывод\u003c/h2\u003e\u003ca id=\"user-content-главный-вывод\" class=\"anchor\" aria-label=\"Permalink: Главный вывод\" href=\"#главный-вывод\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля текущего минимального фронтового API источники распадаются на четыре группы:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003eСтатическая топология приложения.\u003c/li\u003e\n\u003cli\u003eДанные партии и очереди из \u003ccode\u003eBatchQueue\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eСостояние линии из \u003ccode\u003eLine\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eДиагностика камер и принтеров из \u003ccode\u003escada\u003c/code\u003e, плюс прямые поля из \u003ccode\u003ePrinterXX\u003c/code\u003e и \u003ccode\u003eCam...\u003c/code\u003e.\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003eПрактическое правило для backend:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eвсе, что относится к партии, брать в первую очередь из \u003ccode\u003eBatchQueue\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eвсе, что относится к line-level состоянию, брать из \u003ccode\u003eLine\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eвсе, что относится к агрегированной диагностике камер и принтеров, брать из \u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eпрямые поля принтеров и камер брать из их unit-файлов только там, где \u003ccode\u003escada\u003c/code\u003e не содержит нужной информации\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eНо это правило работает только после шага discovery:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eсначала backend определяет, какие устройства реально присутствуют у данного автомата\u003c/li\u003e\n\u003cli\u003eзатем проверяет, какие агрегированные поля реально существуют для этого состава устройств\u003c/li\u003e\n\u003cli\u003eи только потом мапит их в frontend DTO\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eПодтвержденные backend-источники для Grunw11\u003c/h2\u003e\u003ca id=\"user-content-подтвержденные-backend-источники-для-grunw11\" class=\"anchor\" aria-label=\"Permalink: Подтвержденные backend-источники для Grunw11\" href=\"#подтвержденные-backend-источники-для-grunw11\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eНиже перечислено то, что подтверждено именно для \u003ccode\u003eGrunw11\u003c/code\u003e. Для другого автомата этот список может быть короче, длиннее или содержать устройства других типов.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eРеальные unit-источники\u003c/h3\u003e\u003ca id=\"user-content-реальные-unit-источники\" class=\"anchor\" aria-label=\"Permalink: Реальные unit-источники\" href=\"#реальные-unit-источники\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eBatchQueue\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter12\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter13\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter14\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker3\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker4\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eЧто делает \u003ccode\u003escada\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-что-делает-scada\" class=\"anchor\" aria-label=\"Permalink: Что делает scada\" href=\"#что-делает-scada\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля \u003ccode\u003eGrunw11\u003c/code\u003e агрегатор \u003ccode\u003escada\u003c/code\u003e генерирует для фронта подтвержденные диагностические теги:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eDev041...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev042...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev043...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev044...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev071...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev072...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev073...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eDev074...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLineDev011...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLineDev012...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLineDev013...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLineDev014...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elineerr\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eВажно:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eэто не универсальный список полей \u003ccode\u003escada\u003c/code\u003e для всех автоматов\u003c/li\u003e\n\u003cli\u003eпрефиксы \u003ccode\u003eDev041...\u003c/code\u003e, \u003ccode\u003eDev042...\u003c/code\u003e, \u003ccode\u003eDev043...\u003c/code\u003e, \u003ccode\u003eDev044...\u003c/code\u003e появляются только там, где в конфигурации автомата реально есть соответствующие устройства и маппинг в \u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eто же самое относится к \u003ccode\u003eLineDev011...\u003c/code\u003e, \u003ccode\u003eLineDev012...\u003c/code\u003e, \u003ccode\u003eLineDev013...\u003c/code\u003e, \u003ccode\u003eLineDev014...\u003c/code\u003e: число этих групп зависит от числа принтеров на линии\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eМаппинг устройств в SCADA-диагностику\u003c/h3\u003e\u003ca id=\"user-content-маппинг-устройств-в-scada-диагностику\" class=\"anchor\" aria-label=\"Permalink: Маппинг устройств в SCADA-диагностику\" href=\"#маппинг-устройств-в-scada-диагностику\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eКамеры:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation1 -\u0026gt; Dev041\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox1 -\u0026gt; Dev042\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation2 -\u0026gt; Dev043\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox2 -\u0026gt; Dev044\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1 -\u0026gt; Dev071\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker2 -\u0026gt; Dev072\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker3 -\u0026gt; Dev073\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker4 -\u0026gt; Dev074\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПринтеры:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003ePrinter11 -\u0026gt; LineDev011\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter12 -\u0026gt; LineDev012\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter13 -\u0026gt; LineDev013\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter14 -\u0026gt; LineDev014\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e1. REST topology\u003c/h2\u003e\u003ca id=\"user-content-1-rest-topology\" class=\"anchor\" aria-label=\"Permalink: 1. REST topology\" href=\"#1-rest-topology\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGET \u003ccode\u003e/api/v1.0.0/workshops/topology\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-get-apiv100workshopstopology\" class=\"anchor\" aria-label=\"Permalink: GET /api/v1.0.0/workshops/topology\" href=\"#get-apiv100workshopstopology\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eне PrintSrv\u003c/li\u003e\n\u003cli\u003ebackend-конфигурация приложения\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПричина:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eв анализе Grunw11 нет данных о цехах как о сущности верхнего уровня\u003c/li\u003e\n\u003cli\u003ePrintSrv знает устройство и линию, но не прикладную мобильную иерархию цехов\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендация:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eхранить \u003ccode\u003eworkshops\u003c/code\u003e в YAML/БД backend\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПоля:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eОткуда брать\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eid\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003etotalUnits\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config или расчет по unit topology\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGET \u003ccode\u003e/api/v1.0.0/workshops/{workshopId}/units/topology\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-get-apiv100workshopsworkshopidunitstopology\" class=\"anchor\" aria-label=\"Permalink: GET /api/v1.0.0/workshops/{workshopId}/units/topology\" href=\"#get-apiv100workshopsworkshopidunitstopology\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eне PrintSrv напрямую\u003c/li\u003e\n\u003cli\u003ebackend-конфигурация приложения\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПоля:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eОткуда брать\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eid\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eworkshopId\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eunit\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend config\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eGET \u003ccode\u003e/api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-get-apiv100workshopsworkshopidunitsunitiddevicestopology\" class=\"anchor\" aria-label=\"Permalink: GET /api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology\" href=\"#get-apiv100workshopsworkshopidunitsunitiddevicestopology\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLine___Unit0.xml\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eгруппировка по именам устройств\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 подтверждено:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLevel1Printers = Printer11,Printer12,Printer13,Printer14\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLineDevices = CamChecker1,CamChecker2,CamAgregation1,CamAgregation2,CamAgregationBox1,CamAgregationBox2,CamEanChecker1,CamEanChecker2,CamEanChecker3,CamEanChecker4,Printer11,Printer12,Printer13,Printer14\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая сборка ответа для Grunw11:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edevices.printers\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eLine.Level1Printers\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edevices.aggregationCams\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eфильтр \u003ccode\u003eLine.LineDevices\u003c/code\u003e по \u003ccode\u003eCamAgregation1\u003c/code\u003e, \u003ccode\u003eCamAgregation2\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edevices.aggregationBoxCams\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eфильтр \u003ccode\u003eLine.LineDevices\u003c/code\u003e по \u003ccode\u003eCamAgregationBox1\u003c/code\u003e, \u003ccode\u003eCamAgregationBox2\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edevices.checkerCams\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eфильтр \u003ccode\u003eLine.LineDevices\u003c/code\u003e по \u003ccode\u003eCamChecker1\u003c/code\u003e, \u003ccode\u003eCamChecker2\u003c/code\u003e, \u003ccode\u003eCamEanChecker1..4\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eПримечание:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1..4\u003c/code\u003e не имеют отдельной группы в текущем контракте frontend и логично попадают в \u003ccode\u003echeckerCams\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eдля другого аппарата backend не должен хардкодить этот набор; его нужно собирать из фактического \u003ccode\u003eLine.LineDevices\u003c/code\u003e и связанных unit-конфигов\u003c/li\u003e\n\u003cli\u003eактуальный контракт дополнен per-unit раскладкой устройств: \u003ccode\u003egroups\u003c/code\u003e (группы «машина»/«Поток N») и \u003ccode\u003edeviceMeta\u003c/code\u003e (имя на экране + признак счётчиков) настраиваются в админке per-unit (\u003ccode\u003eunit_devices\u003c/code\u003e: \u003ccode\u003edisplay_name\u003c/code\u003e, \u003ccode\u003egroup_label\u003c/code\u003e, \u003ccode\u003edisplay_order\u003c/code\u003e, \u003ccode\u003eshow_counters\u003c/code\u003e, \u003ccode\u003escada_prefix\u003c/code\u003e). Основание и дефолты групп — \u003ca href=\"/savushkin-dev/scada-mobile/blob/main/PRINTSRV_UI_MAP.md\"\u003ePRINTSRV_UI_MAP.md\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e2. WebSocket \u003ccode\u003e/ws/unit/{unitId}\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-2-websocket-wsunitunitid\" class=\"anchor\" aria-label=\"Permalink: 2. WebSocket /ws/unit/{unitId}\" href=\"#2-websocket-wsunitunitid\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eLINE_STATUS\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-line_status\" class=\"anchor\" aria-label=\"Permalink: LINE_STATUS\" href=\"#line_status\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник для каждого поля:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eРекомендуемый источник\u003c/th\u003e\n\u003cth\u003eКомментарий\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003elineName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend topology config\u003c/td\u003e\n\u003ctd\u003eЛучше брать отображаемое имя аппарата, а не имя \u003ccode\u003e.mpr\u003c/code\u003e файла\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003elineState\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eLine.ST\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтвержденный источник\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eshortCode\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.kmc\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.kmc\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eВ текущей документации это наиболее близкий подтвержденный источник короткого кода продукта\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edescription\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.description\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.descr\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eДля линии первичнее \u003ccode\u003eBatchQueue\u003c/code\u003e, т.к. это текущая партия\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eean13\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.ean13\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.ean13\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eАналогично\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003ebatchNumber\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.batch\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.partynumber\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.batch\u003c/code\u003e лучше отражает текущую партию линии\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edateProduced\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.dateproduced\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.dateproduced\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edatePacking\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.datepack\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.datepack\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edateExpiration\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.dateexpiration\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.dateexpiration\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003einitialCounter\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eтребует уточнения, временно \u003ccode\u003ePrinter11.curitem\u003c/code\u003e с парсингом первого сегмента\u003c/td\u003e\n\u003ctd\u003eВ Grunw11 нет отдельного подтвержденного поля именно начального счетчика; \u003ccode\u003eBatchQueue.begincounter\u003c/code\u003e это флаг, а не само числовое значение\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003esite\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.place\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.place\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eСемантически это место/слот на линии\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eitf\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.itf\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.itf\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003ecapacity\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.emk\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.emk\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eВ документации подтверждено как количество единиц в коробе\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eboxCount\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eтребует бизнес-уточнения, кандидаты \u003ccode\u003eBatchQueue.kole\u003c/code\u003e или \u003ccode\u003eBatchQueue.kolm\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПо подтвержденной документации \u003ccode\u003ekole\u003c/code\u003e = количество единиц, \u003ccode\u003ekolm\u003c/code\u003e = количество маркировок; название \u003ccode\u003eboxCount\u003c/code\u003e не совпадает с семантикой 1:1\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003epackageCount\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eтребует бизнес-уточнения, кандидаты \u003ccode\u003eBatchQueue.kolm\u003c/code\u003e или \u003ccode\u003eBatchQueue.kole\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eАналогично\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003efreeze\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.frozen\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eregion\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.region\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edesign\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.designe\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.designe\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eprintDM\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eBatchQueue.printdm\u003c/code\u003e, fallback \u003ccode\u003ePrinter11.printdm\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПодтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eИтог по \u003ccode\u003eLINE_STATUS\u003c/code\u003e:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eпочти весь блок можно собрать из \u003ccode\u003eBatchQueue\u003c/code\u003e + \u003ccode\u003eLine\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter11\u003c/code\u003e нужен как fallback или как источник отдельных принтерных деталей\u003c/li\u003e\n\u003cli\u003eполя \u003ccode\u003einitialCounter\u003c/code\u003e, \u003ccode\u003eboxCount\u003c/code\u003e, \u003ccode\u003epackageCount\u003c/code\u003e сейчас семантически неоднозначны и должны быть подтверждены отдельно\u003c/li\u003e\n\u003cli\u003eналичие fallback-полей в \u003ccode\u003ePrinter11\u003c/code\u003e тоже зависит от аппарата: на некоторых линиях принтеров может быть больше одного, а на некоторых только один\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eDEVICES_STATUS\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-devices_status\" class=\"anchor\" aria-label=\"Permalink: DEVICES_STATUS\" href=\"#devices_status\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЭто самый аппаратозависимый блок во всем контракте. Здесь нельзя предполагать фиксированное количество устройств.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eДля одного аппарата массив \u003ccode\u003eprinters[]\u003c/code\u003e может содержать один объект, для другого два, для третьего четыре и более. То же касается камер: где-то будут только \u003ccode\u003eCamAgregation\u003c/code\u003e и \u003ccode\u003eCamChecker\u003c/code\u003e, где-то добавятся \u003ccode\u003eCamEanChecker1..4\u003c/code\u003e, а где-то набор устройств будет вообще другой.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eСледствие для backend:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eразмеры массивов \u003ccode\u003eprinters\u003c/code\u003e, \u003ccode\u003eaggregationCams\u003c/code\u003e, \u003ccode\u003eaggregationBoxCams\u003c/code\u003e, \u003ccode\u003echeckerCams\u003c/code\u003e должны определяться по topology конкретного аппарата\u003c/li\u003e\n\u003cli\u003eсписок полей \u003ccode\u003escada\u003c/code\u003e, доступных для этих массивов, должен выводиться из фактического device-to-scada mapping, а не из предположения, что всегда существуют \u003ccode\u003eDev041...Dev044\u003c/code\u003e или \u003ccode\u003eLineDev011...LineDev014\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eПринтеры\u003c/h3\u003e\u003ca id=\"user-content-принтеры\" class=\"anchor\" aria-label=\"Permalink: Принтеры\" href=\"#принтеры\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая схема:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003cth\u003eКомментарий\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eprinters[].deviceName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003etopology\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003ePrinter11..14\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eprinters[].state\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003ePrinterXX.ST\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eПрямое состояние принтера\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eprinters[].error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.LineDev0XXError\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eЭто именно тот error-слой, который видит SCADA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eprinters[].batch\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eесли нужен счетчик: \u003ccode\u003ePrinterXX.kolm\u003c/code\u003e; если нужна строка как в SCADA: \u003ccode\u003ePrinterXX.curitem\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eВ текущем фронтовом описании поле названо как счетчик, но в SCADA карточка принтера использует \u003ccode\u003ecuritem\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eПрактическая рекомендация:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eесли фронту нужен именно числовой счетчик, лучше отдать \u003ccode\u003ePrinterXX.kolm\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eесли фронту нужно то же, что в SCADA-блоке принтера, лучше переименовать поле и отдавать \u003ccode\u003ePrinterXX.curitem\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eколичество принтеров на линии брать не из имени \u003ccode\u003ePrinter11\u003c/code\u003e, а из topology аппарата; на другой линии список может закончиться на \u003ccode\u003ePrinter11\u003c/code\u003e, \u003ccode\u003ePrinter12\u003c/code\u003e или иметь иной состав\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eКамеры агрегации на потоке\u003c/h3\u003e\u003ca id=\"user-content-камеры-агрегации-на-потоке\" class=\"anchor\" aria-label=\"Permalink: Камеры агрегации на потоке\" href=\"#камеры-агрегации-на-потоке\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 это:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation2\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая схема:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003cth\u003eПример для Grunw11\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationCams[].deviceName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003etopology\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eCamAgregation1\u003c/code\u003e, \u003ccode\u003eCamAgregation2\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationCams[].read\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev041CounterGeneral\u003c/code\u003e, \u003ccode\u003escada.Dev043CounterGeneral\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eагрегированная статистика SCADA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationCams[].unread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev041CounterMissing\u003c/code\u003e, \u003ccode\u003escada.Dev043CounterMissing\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eагрегированная статистика SCADA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationCams[].state\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev041Work\u003c/code\u003e, \u003ccode\u003escada.Dev043Work\u003c/code\u003e, fallback direct \u003ccode\u003eCam...ST\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eдля UI ближе именно \u003ccode\u003eWork\u003c/code\u003e-флаг из \u003ccode\u003escada\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationCams[].error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev041Error\u003c/code\u003e, \u003ccode\u003escada.Dev043Error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eагрегированная ошибка\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eВажно:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eэто пример именно для \u003ccode\u003eGrunw11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eна аппарате с одной камерой агрегации будет только одна группа полей такого типа\u003c/li\u003e\n\u003cli\u003ebackend должен строить соответствие не по номерам \u003ccode\u003e041\u003c/code\u003e и \u003ccode\u003e043\u003c/code\u003e, а по реальному маппингу устройства в \u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eКамеры агрегации на коробе\u003c/h3\u003e\u003ca id=\"user-content-камеры-агрегации-на-коробе\" class=\"anchor\" aria-label=\"Permalink: Камеры агрегации на коробе\" href=\"#камеры-агрегации-на-коробе\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 это:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox2\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая схема:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003cth\u003eПример для Grunw11\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationBoxCams[].deviceName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003etopology\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eCamAgregationBox1\u003c/code\u003e, \u003ccode\u003eCamAgregationBox2\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationBoxCams[].read\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev042CounterGeneral\u003c/code\u003e, \u003ccode\u003escada.Dev044CounterGeneral\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationBoxCams[].unread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev042CounterMissing\u003c/code\u003e, \u003ccode\u003escada.Dev044CounterMissing\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationBoxCams[].state\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev042Work\u003c/code\u003e, \u003ccode\u003escada.Dev044Work\u003c/code\u003e, fallback direct \u003ccode\u003eCam...ST\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eaggregationBoxCams[].error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev042Error\u003c/code\u003e, \u003ccode\u003escada.Dev044Error\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eВажно:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eна других аппаратах камеры коробочной агрегации могут отсутствовать совсем\u003c/li\u003e\n\u003cli\u003eсоответственно, могут отсутствовать и группы полей \u003ccode\u003eDev042...\u003c/code\u003e или \u003ccode\u003eDev044...\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eChecker-камеры\u003c/h3\u003e\u003ca id=\"user-content-checker-камеры\" class=\"anchor\" aria-label=\"Permalink: Checker-камеры\" href=\"#checker-камеры\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 в \u003ccode\u003echeckerCams\u003c/code\u003e логично включать:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker3\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker4\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eНо источник данных здесь неоднородный.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eИ это еще один аппаратозависимый участок:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eна части аппаратов есть только \u003ccode\u003eCamChecker\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eна части есть и \u003ccode\u003eCamChecker\u003c/code\u003e, и \u003ccode\u003eCamEanChecker1..4\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eна части аппаратов может не быть \u003ccode\u003eCamEanChecker\u003c/code\u003e вообще\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eEAN-checker камеры\u003c/h4\u003e\u003ca id=\"user-content-ean-checker-камеры\" class=\"anchor\" aria-label=\"Permalink: EAN-checker камеры\" href=\"#ean-checker-камеры\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля \u003ccode\u003eCamEanChecker1..4\u003c/code\u003e использовать тот же подход, что и для SCADA:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edeviceName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003etopology\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev071..074CounterGeneral\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eunread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev071..074CounterMissing\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003estate\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev071..074Work\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eerror\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003escada.Dev071..074Error\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eНо только если такие устройства вообще существуют на данном аппарате. Для аппаратов без \u003ccode\u003eCamEanChecker1..4\u003c/code\u003e этих полей в \u003ccode\u003escada\u003c/code\u003e тоже не будет.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eCamChecker1 и CamChecker2\u003c/h4\u003e\u003ca id=\"user-content-camchecker1-и-camchecker2\" class=\"anchor\" aria-label=\"Permalink: CamChecker1 и CamChecker2\" href=\"#camchecker1-и-camchecker2\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля \u003ccode\u003eCamChecker1/2\u003c/code\u003e отдельного \u003ccode\u003eDev0XX\u003c/code\u003e-слоя в \u003ccode\u003escada\u003c/code\u003e для Grunw11 не подтверждено.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eПодтвержденные прямые поля в самих unit-файлах:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBatchSucceeded\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBatchFailed\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003emessage\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLastReadTime\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая схема:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003cth\u003eКомментарий\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edeviceName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003etopology\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eCamCheckerX.BatchSucceeded\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпрямой device-source\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eunread\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eCamCheckerX.BatchFailed\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпрямой device-source\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003estate\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eCamCheckerX.ST\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпрямой device-source\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eerror\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend-вычисление из runtime \u003ccode\u003eerr\u003c/code\u003e или из alert-service\u003c/td\u003e\n\u003ctd\u003eотдельного подтвержденного \u003ccode\u003escada.Dev...Error\u003c/code\u003e для CamChecker1/2 нет\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eИтог по \u003ccode\u003eDEVICES_STATUS\u003c/code\u003e:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eдля принтеров и камер с Dev-мэппингом основным источником диагностики должен быть \u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eдля \u003ccode\u003eCamChecker1/2\u003c/code\u003e backend должен брать прямые счетчики из device-unit и сам вычислять error-флаг\u003c/li\u003e\n\u003cli\u003eколичество объектов в каждом массиве и набор доступных \u003ccode\u003escada\u003c/code\u003e-полей должны вычисляться динамически по конкретному аппарату\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eQUEUE\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-queue\" class=\"anchor\" aria-label=\"Permalink: QUEUE\" href=\"#queue\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eBatchQueue\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eпри возможности лучше опираться на структурированное представление очереди, а не только на display-строки\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПодтвержденные поля:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eItem01..Item10\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eItem01Selected..Item10Selected\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCurItem\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eДополнительный источник, если backend умеет читать его отдельно:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eConfig/Default/Files/BatchQueue.json\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемая схема:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003cth\u003eКомментарий\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eitems[].position\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eиндекс \u003ccode\u003eItem01..Item10\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eподтверждено\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eitems[].shortCode\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпредпочтительно структурированный парсинг из \u003ccode\u003eBatchQueue.json\u003c/code\u003e; fallback парсинг \u003ccode\u003eItemXX\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003eItemXX\u003c/code\u003e это display-строка, формат может быть нестрогим\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eitems[].batch\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпредпочтительно \u003ccode\u003eBatchQueue.json\u003c/code\u003e; fallback парсинг \u003ccode\u003eItemXX\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eitems[].dateProduced\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eпредпочтительно \u003ccode\u003eBatchQueue.json\u003c/code\u003e; fallback парсинг \u003ccode\u003eItemXX\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eПрактическая рекомендация:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eесли backend имеет доступ к \u003ccode\u003eBatchQueue.json\u003c/code\u003e, это более надежный источник для \u003ccode\u003eQUEUE\u003c/code\u003e, чем парсинг строк \u003ccode\u003eItem01..Item10\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eERRORS\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-errors\" class=\"anchor\" aria-label=\"Permalink: ERRORS\" href=\"#errors\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЭтот блок не собирается из одного XML-файла.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eОн требует backend-агрегации из нескольких слоев:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003eruntime-состояние устройств\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada\u003c/code\u003e-теги ошибок\u003c/li\u003e\n\u003cli\u003eсостояние линии\u003c/li\u003e\n\u003cli\u003eжурнал событий\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003edeviceErrors[]\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-deviceerrors\" class=\"anchor\" aria-label=\"Permalink: deviceErrors[]\" href=\"#deviceerrors\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемые источники:\u003c/p\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eОткуда брать\u003c/th\u003e\n\u003cth\u003eКомментарий\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eobjectName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eреальное device-name: \u003ccode\u003ePrinter11\u003c/code\u003e, \u003ccode\u003eCamAgregation1\u003c/code\u003e, \u003ccode\u003eLine\u003c/code\u003e и т.д.\u003c/td\u003e\n\u003ctd\u003eлучше использовать backend-имя устройства, не SCADA-имя виджета\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003epropertyDesc\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend mapping table по флагу ошибки\u003c/td\u003e\n\u003ctd\u003eнапример \u003ccode\u003eLineDev011Error\u003c/code\u003e, \u003ccode\u003eDev041Dublicate\u003c/code\u003e, \u003ccode\u003eDev071Fail\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003evalue\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003eтекущее значение error-флага\u003c/td\u003e\n\u003ctd\u003eобычно \u003ccode\u003e1\u003c/code\u003e/\u003ccode\u003e0\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003edescription\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend human-readable message\u003c/td\u003e\n\u003ctd\u003eиз словаря ошибок или описания состояния\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 подтвержденные error-источники:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003escada.LineDev011Error..LineDev014Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.Dev041Error..Dev044Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.Dev071Error..Dev074Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.ErrorMessage\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eДля других аппаратов этот список должен пересчитываться по фактическому составу устройств. Нельзя считать, что если в коде для \u003ccode\u003eGrunw11\u003c/code\u003e есть \u003ccode\u003eDev041Error\u003c/code\u003e или \u003ccode\u003eLineDev014Error\u003c/code\u003e, то такие же поля будут у любой другой линии.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eДля \u003ccode\u003eCamChecker1/2\u003c/code\u003e error-флаг нужно вычислять backend-ом из runtime-состояния камеры, так как отдельного \u003ccode\u003eDev\u003c/code\u003e-агрегата не подтверждено.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003elogs[]\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-logs\" class=\"anchor\" aria-label=\"Permalink: logs[]\" href=\"#logs\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eПо анализу Grunw11 и существующей документации наиболее реалистичные источники:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003esqlite_logs.db\u003c/code\u003e, если backend использует его как журнал событий\u003c/li\u003e\n\u003cli\u003eлибо файлы логов PrintSrv, если backend читает их напрямую\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eНа уровне минимального фронтового контракта это backend-агрегируемый слой, а не поле из одного unit.\u003c/p\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e3. WebSocket \u003ccode\u003e/ws/live\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-3-websocket-wslive\" class=\"anchor\" aria-label=\"Permalink: 3. WebSocket /ws/live\" href=\"#3-websocket-wslive\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eUNITS_STATUS\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-units_status\" class=\"anchor\" aria-label=\"Permalink: UNITS_STATUS\" href=\"#units_status\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003epayload[].event\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-payloadevent\" class=\"anchor\" aria-label=\"Permalink: payload[].event\" href=\"#payloadevent\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eНе существует как готовое поле в Grunw11 unit-файлах.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eЭто backend-производное представление состояния аппарата.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eРекомендуемые входы для вычисления:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLine.ST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.ErrorMessage\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.lineerr\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПример логики:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eесли есть ошибка линии или активные device-ошибки, отдавать текст ошибки\u003c/li\u003e\n\u003cli\u003eесли \u003ccode\u003eLine.ST = 1\u003c/code\u003e, отдавать состояние типа \u003ccode\u003eВ работе\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eесли \u003ccode\u003eLine.ST != 1\u003c/code\u003e и ошибок нет, отдавать \u003ccode\u003eСтоп\u003c/code\u003e или аналогичный warning-state\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003epayload[].timer\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-payloadtimer\" class=\"anchor\" aria-label=\"Permalink: payload[].timer\" href=\"#payloadtimer\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 в проанализированных PrintSrv-данных подтвержденного прямого поля таймера состояния не найдено.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eРекомендация:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eвычислять backend-ом как время с момента последней смены вычисленного \u003ccode\u003eevent\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eALERT_SNAPSHOT\u003c/code\u003e и \u003ccode\u003eALERT\u003c/code\u003e\u003c/h2\u003e\u003ca id=\"user-content-alert_snapshot-и-alert\" class=\"anchor\" aria-label=\"Permalink: ALERT_SNAPSHOT и ALERT\" href=\"#alert_snapshot-и-alert\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЭто не прямой снимок одного устройства, а результат backend-агрегации.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eКакие поля можно брать\u003c/h3\u003e\u003ca id=\"user-content-какие-поля-можно-брать\" class=\"anchor\" aria-label=\"Permalink: Какие поля можно брать\" href=\"#какие-поля-можно-брать\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cmarkdown-accessiblity-table\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eПоле API\u003c/th\u003e\n\u003cth\u003eИсточник\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eworkshopId\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend topology config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eunitId\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend topology config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eunitName\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend topology config\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003eactive\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend alert engine\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003ccode\u003etimestamp\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003ebackend alert engine\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\u003c/markdown-accessiblity-table\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eИз чего собирать сами алерты\u003c/h3\u003e\u003ca id=\"user-content-из-чего-собирать-сами-алерты\" class=\"anchor\" aria-label=\"Permalink: Из чего собирать сами алерты\" href=\"#из-чего-собирать-сами-алерты\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля Grunw11 подтвержденные источники для alert engine:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLine.Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.ErrorMessage\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine.ST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.lineerr\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.LineDev011Error..014Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.Dev041Error..044Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada.Dev071Error..074Error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eruntime \u003ccode\u003eerr\u003c/code\u003e для устройств без \u003ccode\u003escada\u003c/code\u003e-обертки, например \u003ccode\u003eCamChecker1/2\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eДля других аппаратов backend должен собирать список alert-источников по фактическим unit-объектам и реально существующим \u003ccode\u003escada\u003c/code\u003e-полям, а не по фиксированному шаблону \u003ccode\u003eGrunw11\u003c/code\u003e.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eerrors[].device\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-errorsdevice\" class=\"anchor\" aria-label=\"Permalink: errors[].device\" href=\"#errorsdevice\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eРекомендуется отдавать реальные backend-имена устройств:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003ePrinter11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter12\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eНе рекомендуется отдавать:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eConnectionPrinter11\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCMSDev041\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecdev41err\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eПричина:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eэто экранные объекты SCADA, а не реальные backend-устройства\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eerrors[].code\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-errorscode\" class=\"anchor\" aria-label=\"Permalink: errors[].code\" href=\"#errorscode\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLine.Error\u003c/code\u003e или device-specific \u003ccode\u003emessage\u003c/code\u003e, если есть код ошибки\u003c/li\u003e\n\u003cli\u003eесли сообщение warning-класса и числового кода нет, можно отдавать \u003ccode\u003e0\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eerrors[].message\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-errorsmessage\" class=\"anchor\" aria-label=\"Permalink: errors[].message\" href=\"#errorsmessage\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eИсточник:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eLine.ErrorMessage\u003c/code\u003e, если ошибка относится к линии\u003c/li\u003e\n\u003cli\u003ebackend-словарь интерпретации кодов device/message-флагов\u003c/li\u003e\n\u003cli\u003efallback: человекочитаемое описание сформированного alert-condition\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e\u003ccode\u003eseverity\u003c/code\u003e\u003c/h3\u003e\u003ca id=\"user-content-severity\" class=\"anchor\" aria-label=\"Permalink: severity\" href=\"#severity\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЭто backend-вычисляемое поле.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eПод текущую модель Grunw11 логично:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eCritical\u003c/code\u003e, если есть активная ошибка линии или устройства\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eWarning\u003c/code\u003e, если линия не работает, но явной ошибки нет\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003e4. Что можно брать уже сейчас без спорных мест\u003c/h2\u003e\u003ca id=\"user-content-4-что-можно-брать-уже-сейчас-без-спорных-мест\" class=\"anchor\" aria-label=\"Permalink: 4. Что можно брать уже сейчас без спорных мест\" href=\"#4-что-можно-брать-уже-сейчас-без-спорных-мест\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eНиже список полей, для которых источник по Grunw11 подтвержден достаточно уверенно.\u003c/p\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eМожно забирать сразу\u003c/h3\u003e\u003ca id=\"user-content-можно-забирать-сразу\" class=\"anchor\" aria-label=\"Permalink: Можно забирать сразу\" href=\"#можно-забирать-сразу\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eНиже список полей, которые для \u003ccode\u003eGrunw11\u003c/code\u003e подтверждены уверенно. Этот блок не означает, что те же самые поля обязательно существуют у любого другого аппарата в том же объеме.\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003elineState -\u0026gt; Line.ST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edescription -\u0026gt; BatchQueue.description\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eean13 -\u0026gt; BatchQueue.ean13\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ebatchNumber -\u0026gt; BatchQueue.batch\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edateProduced -\u0026gt; BatchQueue.dateproduced\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edatePacking -\u0026gt; BatchQueue.datepack\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edateExpiration -\u0026gt; BatchQueue.dateexpiration\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003esite -\u0026gt; BatchQueue.place\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eitf -\u0026gt; BatchQueue.itf\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecapacity -\u0026gt; BatchQueue.emk\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003efreeze -\u0026gt; BatchQueue.frozen\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eregion -\u0026gt; BatchQueue.region\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edesign -\u0026gt; BatchQueue.designe\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprintDM -\u0026gt; BatchQueue.printdm\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprinters[].state -\u0026gt; PrinterXX.ST\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eprinters[].error -\u0026gt; scada.LineDev0XXError\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eaggregationCams[].read/unread/error -\u0026gt; scada.Dev041/043...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eaggregationBoxCams[].read/unread/error -\u0026gt; scada.Dev042/044...\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eean-checker state/read/unread/error -\u0026gt; scada.Dev071..074...\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eТребуют отдельного согласования\u003c/h3\u003e\u003ca id=\"user-content-требуют-отдельного-согласования\" class=\"anchor\" aria-label=\"Permalink: Требуют отдельного согласования\" href=\"#требуют-отдельного-согласования\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003einitialCounter\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eboxCount\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epackageCount\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUNITS_STATUS.payload[].event\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUNITS_STATUS.payload[].timer\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker1/2.error\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eструктура \u003ccode\u003eQUEUE.items[]\u003c/code\u003e, если не читать \u003ccode\u003eBatchQueue.json\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eИтоговая рекомендация для backend\u003c/h2\u003e\u003ca id=\"user-content-итоговая-рекомендация-для-backend\" class=\"anchor\" aria-label=\"Permalink: Итоговая рекомендация для backend\" href=\"#итоговая-рекомендация-для-backend\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eДля минимального frontend-контракта backend должен строить ответ из следующих базовых источников:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003etopology config приложения\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eBatchQueue\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eLine\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePrinter11..14\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregation1..2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamAgregationBox1..2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamChecker1..2\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCamEanChecker1..4\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003eНо это перечень для \u003ccode\u003eGrunw11\u003c/code\u003e, а не для всех аппаратов. В общем случае правильный порядок работы такой:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003eопределить состав устройств конкретного аппарата\u003c/li\u003e\n\u003cli\u003eопределить, какие unit-объекты реально существуют в PrintSrv для этого аппарата\u003c/li\u003e\n\u003cli\u003eопределить, какие агрегированные поля реально публикует \u003ccode\u003escada\u003c/code\u003e для этого состава устройств\u003c/li\u003e\n\u003cli\u003eтолько после этого собирать frontend DTO\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp dir=\"auto\"\u003eИ отдельно должен иметь собственный слой агрегации для:\u003c/p\u003e\n\u003col dir=\"auto\"\u003e\n\u003cli\u003e\u003ccode\u003eALERT\u003c/code\u003e и \u003ccode\u003eALERT_SNAPSHOT\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUNITS_STATUS.event\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eUNITS_STATUS.timer\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eERRORS.deviceErrors[]\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eQUEUE.items[]\u003c/code\u003e, если нужна не display-строка, а структурированная очередь\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eКороткий практический вывод\u003c/h2\u003e\u003ca id=\"user-content-короткий-практический-вывод\" class=\"anchor\" aria-label=\"Permalink: Короткий практический вывод\" href=\"#короткий-практический-вывод\"\u003e\u003csvg data-component=\"Octicon\" class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eЕсли смотреть только на то, что подтверждено по Grunw11, то frontend сейчас лучше всего кормить так:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eкарточку партии собирать в первую очередь из \u003ccode\u003eBatchQueue\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eдиагностику устройств брать из \u003ccode\u003escada\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eпрямые статусы принтеров брать из \u003ccode\u003ePrinterXX\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eпрямые статусы \u003ccode\u003eCamChecker1/2\u003c/code\u003e, где нет \u003ccode\u003eDev\u003c/code\u003e-обертки, брать из самих camera-unit\u003c/li\u003e\n\u003cli\u003eвсе человекочитаемые event/alert/log представления вычислять в backend, а не пытаться читать как готовые поля PrintSrv\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eИ главное уточнение: это не означает, что на каждом аппарате обязательно будут все эти сущности и все эти поля. У одного аппарата может быть один принтер и одна камера, у другого два принтера и четыре EAN-checker камеры, у третьего иной состав устройств. Поэтому backend должен опираться на discovery конкретного аппарата, а не на фиксированный список полей из \u003ccode\u003eGrunw11\u003c/code\u003e.\u003c/p\u003e\n\u003c/article\u003e","richTextTruncated":false,"renderedFileInfo":null,"symbols":{"timed_out":false,"not_analyzed":false,"symbols":[{"name":"Источники данных для фронтенда","fully_qualified_name":"Источники данных для фронтенда","kind":"section_1","ident_start":2,"ident_end":59,"extent_start":0,"extent_end":33940,"ident_utf16":{"start":{"line_number":0,"utf16_col":2},"end":{"line_number":0,"utf16_col":32}},"extent_utf16":{"start":{"line_number":0,"utf16_col":0},"end":{"line_number":655,"utf16_col":0}}},{"name":"Purpose","fully_qualified_name":"Purpose","kind":"section_2","ident_start":64,"ident_end":71,"extent_start":61,"extent_end":232,"ident_utf16":{"start":{"line_number":2,"utf16_col":3},"end":{"line_number":2,"utf16_col":10}},"extent_utf16":{"start":{"line_number":2,"utf16_col":0},"end":{"line_number":5,"utf16_col":0}}},{"name":"Table of contents","fully_qualified_name":"Table of contents","kind":"section_2","ident_start":235,"ident_end":252,"extent_start":232,"extent_end":755,"ident_utf16":{"start":{"line_number":5,"utf16_col":3},"end":{"line_number":5,"utf16_col":20}},"extent_utf16":{"start":{"line_number":5,"utf16_col":0},"end":{"line_number":13,"utf16_col":0}}},{"name":"Что это за документ","fully_qualified_name":"Что это за документ","kind":"section_2","ident_start":758,"ident_end":793,"extent_start":755,"extent_end":1893,"ident_utf16":{"start":{"line_number":13,"utf16_col":3},"end":{"line_number":13,"utf16_col":22}},"extent_utf16":{"start":{"line_number":13,"utf16_col":0},"end":{"line_number":25,"utf16_col":0}}},{"name":"Важная оговорка по области применимости","fully_qualified_name":"Важная оговорка по области применимости","kind":"section_2","ident_start":1896,"ident_end":1970,"extent_start":1893,"extent_end":4195,"ident_utf16":{"start":{"line_number":25,"utf16_col":3},"end":{"line_number":25,"utf16_col":42}},"extent_utf16":{"start":{"line_number":25,"utf16_col":0},"end":{"line_number":51,"utf16_col":0}}},{"name":"Главный вывод","fully_qualified_name":"Главный вывод","kind":"section_2","ident_start":4198,"ident_end":4223,"extent_start":4195,"extent_end":5769,"ident_utf16":{"start":{"line_number":51,"utf16_col":3},"end":{"line_number":51,"utf16_col":16}},"extent_utf16":{"start":{"line_number":51,"utf16_col":0},"end":{"line_number":75,"utf16_col":0}}},{"name":"Подтвержденные backend-источники для Grunw11","fully_qualified_name":"Подтвержденные backend-источники для Grunw11","kind":"section_2","ident_start":5772,"ident_end":5842,"extent_start":5769,"extent_end":7904,"ident_utf16":{"start":{"line_number":75,"utf16_col":3},"end":{"line_number":75,"utf16_col":47}},"extent_utf16":{"start":{"line_number":75,"utf16_col":0},"end":{"line_number":145,"utf16_col":0}}},{"name":"Реальные unit-источники","fully_qualified_name":"Реальные unit-источники","kind":"section_3","ident_start":6132,"ident_end":6172,"extent_start":6128,"extent_end":6455,"ident_utf16":{"start":{"line_number":79,"utf16_col":4},"end":{"line_number":79,"utf16_col":27}},"extent_utf16":{"start":{"line_number":79,"utf16_col":0},"end":{"line_number":99,"utf16_col":0}}},{"name":"Что делает `scada`","fully_qualified_name":"Что делает `scada`","kind":"section_3","ident_start":6459,"ident_end":6486,"extent_start":6455,"extent_end":7442,"ident_utf16":{"start":{"line_number":99,"utf16_col":4},"end":{"line_number":99,"utf16_col":22}},"extent_utf16":{"start":{"line_number":99,"utf16_col":0},"end":{"line_number":123,"utf16_col":0}}},{"name":"Маппинг устройств в SCADA-диагностику","fully_qualified_name":"Маппинг устройств в SCADA-диагностику","kind":"section_3","ident_start":7446,"ident_end":7511,"extent_start":7442,"extent_end":7904,"ident_utf16":{"start":{"line_number":123,"utf16_col":4},"end":{"line_number":123,"utf16_col":41}},"extent_utf16":{"start":{"line_number":123,"utf16_col":0},"end":{"line_number":145,"utf16_col":0}}},{"name":"1. REST topology","fully_qualified_name":"1. REST topology","kind":"section_2","ident_start":7907,"ident_end":7923,"extent_start":7904,"extent_end":10838,"ident_utf16":{"start":{"line_number":145,"utf16_col":3},"end":{"line_number":145,"utf16_col":19}},"extent_utf16":{"start":{"line_number":145,"utf16_col":0},"end":{"line_number":215,"utf16_col":0}}},{"name":"GET `/api/v1.0.0/workshops/topology`","fully_qualified_name":"GET `/api/v1.0.0/workshops/topology`","kind":"section_3","ident_start":7929,"ident_end":7965,"extent_start":7925,"extent_end":8602,"ident_utf16":{"start":{"line_number":147,"utf16_col":4},"end":{"line_number":147,"utf16_col":40}},"extent_utf16":{"start":{"line_number":147,"utf16_col":0},"end":{"line_number":171,"utf16_col":0}}},{"name":"GET `/api/v1.0.0/workshops/{workshopId}/units/topology`","fully_qualified_name":"GET `/api/v1.0.0/workshops/{workshopId}/units/topology`","kind":"section_3","ident_start":8606,"ident_end":8661,"extent_start":8602,"extent_end":8925,"ident_utf16":{"start":{"line_number":171,"utf16_col":4},"end":{"line_number":171,"utf16_col":59}},"extent_utf16":{"start":{"line_number":171,"utf16_col":0},"end":{"line_number":186,"utf16_col":0}}},{"name":"GET `/api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology`","fully_qualified_name":"GET `/api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology`","kind":"section_3","ident_start":8929,"ident_end":9001,"extent_start":8925,"extent_end":10838,"ident_utf16":{"start":{"line_number":186,"utf16_col":4},"end":{"line_number":186,"utf16_col":76}},"extent_utf16":{"start":{"line_number":186,"utf16_col":0},"end":{"line_number":215,"utf16_col":0}}},{"name":"2. WebSocket `/ws/unit/{unitId}`","fully_qualified_name":"2. WebSocket `/ws/unit/{unitId}`","kind":"section_2","ident_start":10841,"ident_end":10873,"extent_start":10838,"extent_end":10875,"ident_utf16":{"start":{"line_number":215,"utf16_col":3},"end":{"line_number":215,"utf16_col":35}},"extent_utf16":{"start":{"line_number":215,"utf16_col":0},"end":{"line_number":217,"utf16_col":0}}},{"name":"`LINE_STATUS`","fully_qualified_name":"`LINE_STATUS`","kind":"section_2","ident_start":10878,"ident_end":10891,"extent_start":10875,"extent_end":14516,"ident_utf16":{"start":{"line_number":217,"utf16_col":3},"end":{"line_number":217,"utf16_col":16}},"extent_utf16":{"start":{"line_number":217,"utf16_col":0},"end":{"line_number":252,"utf16_col":0}}},{"name":"`DEVICES_STATUS`","fully_qualified_name":"`DEVICES_STATUS`","kind":"section_2","ident_start":14519,"ident_end":14535,"extent_start":14516,"extent_end":22032,"ident_utf16":{"start":{"line_number":252,"utf16_col":3},"end":{"line_number":252,"utf16_col":19}},"extent_utf16":{"start":{"line_number":252,"utf16_col":0},"end":{"line_number":388,"utf16_col":0}}},{"name":"Принтеры","fully_qualified_name":"Принтеры","kind":"section_3","ident_start":15737,"ident_end":15753,"extent_start":15733,"extent_end":17113,"ident_utf16":{"start":{"line_number":263,"utf16_col":4},"end":{"line_number":263,"utf16_col":12}},"extent_utf16":{"start":{"line_number":263,"utf16_col":0},"end":{"line_number":280,"utf16_col":0}}},{"name":"Камеры агрегации на потоке","fully_qualified_name":"Камеры агрегации на потоке","kind":"section_3","ident_start":17117,"ident_end":17166,"extent_start":17113,"extent_end":18411,"ident_utf16":{"start":{"line_number":280,"utf16_col":4},"end":{"line_number":280,"utf16_col":30}},"extent_utf16":{"start":{"line_number":280,"utf16_col":0},"end":{"line_number":303,"utf16_col":0}}},{"name":"Камеры агрегации на коробе","fully_qualified_name":"Камеры агрегации на коробе","kind":"section_3","ident_start":18415,"ident_end":18464,"extent_start":18411,"extent_end":19407,"ident_utf16":{"start":{"line_number":303,"utf16_col":4},"end":{"line_number":303,"utf16_col":30}},"extent_utf16":{"start":{"line_number":303,"utf16_col":0},"end":{"line_number":325,"utf16_col":0}}},{"name":"Checker-камеры","fully_qualified_name":"Checker-камеры","kind":"section_3","ident_start":19411,"ident_end":19431,"extent_start":19407,"extent_end":22032,"ident_utf16":{"start":{"line_number":325,"utf16_col":4},"end":{"line_number":325,"utf16_col":18}},"extent_utf16":{"start":{"line_number":325,"utf16_col":0},"end":{"line_number":388,"utf16_col":0}}},{"name":"EAN-checker камеры","fully_qualified_name":"EAN-checker камеры","kind":"section_4","ident_start":19995,"ident_end":20019,"extent_start":19990,"extent_end":20612,"ident_utf16":{"start":{"line_number":344,"utf16_col":5},"end":{"line_number":344,"utf16_col":23}},"extent_utf16":{"start":{"line_number":344,"utf16_col":0},"end":{"line_number":358,"utf16_col":0}}},{"name":"CamChecker1 и CamChecker2","fully_qualified_name":"CamChecker1 и CamChecker2","kind":"section_4","ident_start":20617,"ident_end":20643,"extent_start":20612,"extent_end":22032,"ident_utf16":{"start":{"line_number":358,"utf16_col":5},"end":{"line_number":358,"utf16_col":30}},"extent_utf16":{"start":{"line_number":358,"utf16_col":0},"end":{"line_number":388,"utf16_col":0}}},{"name":"`QUEUE`","fully_qualified_name":"`QUEUE`","kind":"section_2","ident_start":22035,"ident_end":22042,"extent_start":22032,"extent_end":23450,"ident_utf16":{"start":{"line_number":388,"utf16_col":3},"end":{"line_number":388,"utf16_col":10}},"extent_utf16":{"start":{"line_number":388,"utf16_col":0},"end":{"line_number":420,"utf16_col":0}}},{"name":"`ERRORS`","fully_qualified_name":"`ERRORS`","kind":"section_2","ident_start":23453,"ident_end":23461,"extent_start":23450,"extent_end":25837,"ident_utf16":{"start":{"line_number":420,"utf16_col":3},"end":{"line_number":420,"utf16_col":11}},"extent_utf16":{"start":{"line_number":420,"utf16_col":0},"end":{"line_number":465,"utf16_col":0}}},{"name":"`deviceErrors[]`","fully_qualified_name":"`deviceErrors[]`","kind":"section_3","ident_start":23779,"ident_end":23795,"extent_start":23775,"extent_end":25303,"ident_utf16":{"start":{"line_number":431,"utf16_col":4},"end":{"line_number":431,"utf16_col":20}},"extent_utf16":{"start":{"line_number":431,"utf16_col":0},"end":{"line_number":454,"utf16_col":0}}},{"name":"`logs[]`","fully_qualified_name":"`logs[]`","kind":"section_3","ident_start":25307,"ident_end":25315,"extent_start":25303,"extent_end":25837,"ident_utf16":{"start":{"line_number":454,"utf16_col":4},"end":{"line_number":454,"utf16_col":12}},"extent_utf16":{"start":{"line_number":454,"utf16_col":0},"end":{"line_number":465,"utf16_col":0}}},{"name":"3. WebSocket `/ws/live`","fully_qualified_name":"3. WebSocket `/ws/live`","kind":"section_2","ident_start":25840,"ident_end":25863,"extent_start":25837,"extent_end":25865,"ident_utf16":{"start":{"line_number":465,"utf16_col":3},"end":{"line_number":465,"utf16_col":26}},"extent_utf16":{"start":{"line_number":465,"utf16_col":0},"end":{"line_number":467,"utf16_col":0}}},{"name":"`UNITS_STATUS`","fully_qualified_name":"`UNITS_STATUS`","kind":"section_2","ident_start":25868,"ident_end":25882,"extent_start":25865,"extent_end":26981,"ident_utf16":{"start":{"line_number":467,"utf16_col":3},"end":{"line_number":467,"utf16_col":17}},"extent_utf16":{"start":{"line_number":467,"utf16_col":0},"end":{"line_number":498,"utf16_col":0}}},{"name":"`payload[].event`","fully_qualified_name":"`payload[].event`","kind":"section_3","ident_start":25888,"ident_end":25905,"extent_start":25884,"extent_end":26604,"ident_utf16":{"start":{"line_number":469,"utf16_col":4},"end":{"line_number":469,"utf16_col":21}},"extent_utf16":{"start":{"line_number":469,"utf16_col":0},"end":{"line_number":488,"utf16_col":0}}},{"name":"`payload[].timer`","fully_qualified_name":"`payload[].timer`","kind":"section_3","ident_start":26608,"ident_end":26625,"extent_start":26604,"extent_end":26981,"ident_utf16":{"start":{"line_number":488,"utf16_col":4},"end":{"line_number":488,"utf16_col":21}},"extent_utf16":{"start":{"line_number":488,"utf16_col":0},"end":{"line_number":498,"utf16_col":0}}},{"name":"`ALERT_SNAPSHOT` и `ALERT`","fully_qualified_name":"`ALERT_SNAPSHOT` и `ALERT`","kind":"section_2","ident_start":26984,"ident_end":27011,"extent_start":26981,"extent_end":29465,"ident_utf16":{"start":{"line_number":498,"utf16_col":3},"end":{"line_number":498,"utf16_col":29}},"extent_utf16":{"start":{"line_number":498,"utf16_col":0},"end":{"line_number":573,"utf16_col":0}}},{"name":"Какие поля можно брать","fully_qualified_name":"Какие поля можно брать","kind":"section_3","ident_start":27141,"ident_end":27182,"extent_start":27137,"extent_end":27429,"ident_utf16":{"start":{"line_number":502,"utf16_col":4},"end":{"line_number":502,"utf16_col":26}},"extent_utf16":{"start":{"line_number":502,"utf16_col":0},"end":{"line_number":512,"utf16_col":0}}},{"name":"Из чего собирать сами алерты","fully_qualified_name":"Из чего собирать сами алерты","kind":"section_3","ident_start":27433,"ident_end":27485,"extent_start":27429,"extent_end":28145,"ident_utf16":{"start":{"line_number":512,"utf16_col":4},"end":{"line_number":512,"utf16_col":32}},"extent_utf16":{"start":{"line_number":512,"utf16_col":0},"end":{"line_number":527,"utf16_col":0}}},{"name":"`errors[].device`","fully_qualified_name":"`errors[].device`","kind":"section_3","ident_start":28149,"ident_end":28166,"extent_start":28145,"extent_end":28569,"ident_utf16":{"start":{"line_number":527,"utf16_col":4},"end":{"line_number":527,"utf16_col":21}},"extent_utf16":{"start":{"line_number":527,"utf16_col":0},"end":{"line_number":547,"utf16_col":0}}},{"name":"`errors[].code`","fully_qualified_name":"`errors[].code`","kind":"section_3","ident_start":28573,"ident_end":28588,"extent_start":28569,"extent_end":28819,"ident_utf16":{"start":{"line_number":547,"utf16_col":4},"end":{"line_number":547,"utf16_col":19}},"extent_utf16":{"start":{"line_number":547,"utf16_col":0},"end":{"line_number":554,"utf16_col":0}}},{"name":"`errors[].message`","fully_qualified_name":"`errors[].message`","kind":"section_3","ident_start":28823,"ident_end":28841,"extent_start":28819,"extent_end":29141,"ident_utf16":{"start":{"line_number":554,"utf16_col":4},"end":{"line_number":554,"utf16_col":22}},"extent_utf16":{"start":{"line_number":554,"utf16_col":0},"end":{"line_number":562,"utf16_col":0}}},{"name":"`severity`","fully_qualified_name":"`severity`","kind":"section_3","ident_start":29145,"ident_end":29155,"extent_start":29141,"extent_end":29465,"ident_utf16":{"start":{"line_number":562,"utf16_col":4},"end":{"line_number":562,"utf16_col":14}},"extent_utf16":{"start":{"line_number":562,"utf16_col":0},"end":{"line_number":573,"utf16_col":0}}},{"name":"4. Что можно брать уже сейчас без спорных мест","fully_qualified_name":"4. Что можно брать уже сейчас без спорных мест","kind":"section_2","ident_start":29468,"ident_end":29550,"extent_start":29465,"extent_end":31106,"ident_utf16":{"start":{"line_number":573,"utf16_col":3},"end":{"line_number":573,"utf16_col":49}},"extent_utf16":{"start":{"line_number":573,"utf16_col":0},"end":{"line_number":613,"utf16_col":0}}},{"name":"Можно забирать сразу","fully_qualified_name":"Можно забирать сразу","kind":"section_3","ident_start":29705,"ident_end":29743,"extent_start":29701,"extent_end":30812,"ident_utf16":{"start":{"line_number":577,"utf16_col":4},"end":{"line_number":577,"utf16_col":24}},"extent_utf16":{"start":{"line_number":577,"utf16_col":0},"end":{"line_number":601,"utf16_col":0}}},{"name":"Требуют отдельного согласования","fully_qualified_name":"Требуют отдельного согласования","kind":"section_3","ident_start":30816,"ident_end":30876,"extent_start":30812,"extent_end":31106,"ident_utf16":{"start":{"line_number":601,"utf16_col":4},"end":{"line_number":601,"utf16_col":35}},"extent_utf16":{"start":{"line_number":601,"utf16_col":0},"end":{"line_number":613,"utf16_col":0}}},{"name":"Итоговая рекомендация для backend","fully_qualified_name":"Итоговая рекомендация для backend","kind":"section_2","ident_start":31109,"ident_end":31165,"extent_start":31106,"extent_end":32513,"ident_utf16":{"start":{"line_number":613,"utf16_col":3},"end":{"line_number":613,"utf16_col":36}},"extent_utf16":{"start":{"line_number":613,"utf16_col":0},"end":{"line_number":644,"utf16_col":0}}},{"name":"Короткий практический вывод","fully_qualified_name":"Короткий практический вывод","kind":"section_2","ident_start":32516,"ident_end":32568,"extent_start":32513,"extent_end":33940,"ident_utf16":{"start":{"line_number":644,"utf16_col":3},"end":{"line_number":644,"utf16_col":30}},"extent_utf16":{"start":{"line_number":644,"utf16_col":0},"end":{"line_number":655,"utf16_col":0}}}]}},"codeViewLayoutRoute":{"repo":{"id":1078098970,"defaultBranch":"main","name":"scada-mobile","ownerLogin":"savushkin-dev","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2025-10-17T07:54:20.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/220682001?v=4","public":true,"private":false,"isOrgOwned":true,"isArchived":false},"currentUser":null,"uploadToken":"xzMlbxZSoW6xPa-BmC_2fKrfbiY7BuZvJdaDv6bNF5nF4sONb1Bg6Rzv37_FUp-GwEBdrkKDRllzaZlqFkHOMQ","allShortcutsEnabled":false,"treeExpanded":true,"path":"FRONTEND_DATA_SOURCES.md","symbolsExpanded":false,"refInfo":{"name":"main","listCacheKey":"v0:1790843568.0","canEdit":false,"currentOid":"d1869d73941a8ab09774b6a43deb817668bebd57"},"helpUrl":"https://docs.github.com","githubDevUrl":null},"codeViewFileTreeLayoutRoute":{"fileTree":{"":{"items":[{"name":".github","path":".github","contentType":"directory"},{"name":".kimi-code","path":".kimi-code","contentType":"directory"},{"name":".kimi","path":".kimi","contentType":"directory"},{"name":"backend","path":"backend","contentType":"directory"},{"name":"frontend","path":"frontend","contentType":"directory"},{"name":"load-tests","path":"load-tests","contentType":"directory"},{"name":"printsrv-logs","path":"printsrv-logs","contentType":"directory"},{"name":"screenshots-from-pm","path":"screenshots-from-pm","contentType":"directory"},{"name":"screenshots","path":"screenshots","contentType":"directory"},{"name":"scripts","path":"scripts","contentType":"directory"},{"name":".dockerignore","path":".dockerignore","contentType":"file"},{"name":".editorconfig","path":".editorconfig","contentType":"file"},{"name":".env.prod.example","path":".env.prod.example","contentType":"file"},{"name":".gitattributes","path":".gitattributes","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":"AGENTS.md","path":"AGENTS.md","contentType":"file"},{"name":"ALERT_LIFECYCLE.md","path":"ALERT_LIFECYCLE.md","contentType":"file"},{"name":"API_REFERENCE.md","path":"API_REFERENCE.md","contentType":"file"},{"name":"AUTH_FLOW.md","path":"AUTH_FLOW.md","contentType":"file"},{"name":"BACKEND_ARCHITECTURE.md","path":"BACKEND_ARCHITECTURE.md","contentType":"file"},{"name":"BACKEND_COMPONENT_DIAGRAM.md","path":"BACKEND_COMPONENT_DIAGRAM.md","contentType":"file"},{"name":"BACKEND_DATA_FLOW.md","path":"BACKEND_DATA_FLOW.md","contentType":"file"},{"name":"FRONTEND_API.md","path":"FRONTEND_API.md","contentType":"file"},{"name":"FRONTEND_ARCHITECTURE.md","path":"FRONTEND_ARCHITECTURE.md","contentType":"file"},{"name":"FRONTEND_DATA_SOURCES.md","path":"FRONTEND_DATA_SOURCES.md","contentType":"file"},{"name":"MAKEFILE.md","path":"MAKEFILE.md","contentType":"file"},{"name":"Makefile","path":"Makefile","contentType":"file"},{"name":"NOTIFICATIONS_ARCHITECTURE.md","path":"NOTIFICATIONS_ARCHITECTURE.md","contentType":"file"},{"name":"PRINTSRV_UI_MAP.md","path":"PRINTSRV_UI_MAP.md","contentType":"file"},{"name":"PRINTSR_USED_TAGS.md","path":"PRINTSR_USED_TAGS.md","contentType":"file"},{"name":"PROJECT_DIAGRAM.md","path":"PROJECT_DIAGRAM.md","contentType":"file"},{"name":"README.en.md","path":"README.en.md","contentType":"file"},{"name":"README.md","path":"README.md","contentType":"file"},{"name":"RUN_PROJECT_DOCKER.md","path":"RUN_PROJECT_DOCKER.md","contentType":"file"},{"name":"SCADA_MACHINE_API.md","path":"SCADA_MACHINE_API.md","contentType":"file"},{"name":"SECURITY.md","path":"SECURITY.md","contentType":"file"},{"name":"STRUCTURE.md","path":"STRUCTURE.md","contentType":"file"},{"name":"TEST_ACCOUNTS.md","path":"TEST_ACCOUNTS.md","contentType":"file"},{"name":"api_mapping.md","path":"api_mapping.md","contentType":"file"},{"name":"docker-compose.yml","path":"docker-compose.yml","contentType":"file"},{"name":"ПЛАН_группы_устройств_и_счетчики.md","path":"ПЛАН_группы_устройств_и_счетчики.md","contentType":"file"},{"name":"ТЗ_Gateway_Якимовец.md","path":"ТЗ_Gateway_Якимовец.md","contentType":"file"},{"name":"ТЗ_исправление_runtime_счетчиков_и_ошибок.md","path":"ТЗ_исправление_runtime_счетчиков_и_ошибок.md","contentType":"file"},{"name":"ТЗ_миграция_на_Kafka.md","path":"ТЗ_миграция_на_Kafka.md","contentType":"file"},{"name":"ЧЕКЛИСТ_приёмки_2026-09-25.md","path":"ЧЕКЛИСТ_приёмки_2026-09-25.md","contentType":"file"}],"totalCount":45}},"fileTreeProcessingTime":71.07982200000001,"foldersToFetch":[]},"codeViewBlobLayoutRoute":{"codeLineWrapEnabled":false,"refInfo":{"name":"main","listCacheKey":"v0:1790843568.0","canEdit":false,"refType":"branch","currentOid":"d1869d73941a8ab09774b6a43deb817668bebd57","canEditOnDefaultBranch":false,"fileExistsOnDefault":true},"path":"FRONTEND_DATA_SOURCES.md","blob":{"copilotSWEAgentEnabled":false,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/savushkin-dev/scada-mobile/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null},"displayName":"FRONTEND_DATA_SOURCES.md","displayUrl":"https://github.com/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md?raw=true","headerInfo":{"blobSize":"33.1 KB","deleteTooltip":"You must be signed in to make or propose changes","editTooltip":"You must be signed in to make or propose changes","ghDesktopPath":"https://desktop.github.com","isGitLfs":false,"onBranch":true,"shortPath":"dbc6502","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Fsavushkin-dev%2Fscada-mobile%2Fblob%2Fmain%2FFRONTEND_DATA_SOURCES.md","isCSV":false,"isRichtext":true,"lineInfo":{"truncatedLoc":"655","truncatedSloc":"452"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"isIssueTemplate":false,"isDiscussionTemplate":false,"language":"Markdown","languageID":222,"large":false,"planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/savushkin-dev/scada-mobile/blob/main/FRONTEND_DATA_SOURCES.md","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","releasePath":"/savushkin-dev/scada-mobile/releases/new?marketplace=true","showPublishActionBanner":false},"rawBlobUrl":"https://github.com/savushkin-dev/scada-mobile/raw/refs/heads/main/FRONTEND_DATA_SOURCES.md","renderImageOrRaw":false,"shortPath":null,"symbolsEnabled":true,"tabSize":4,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":null,"showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null},"copilotInfo":null,"copilotAccessAllowed":false,"copilotSpacesEnabled":false,"modelsAccessAllowed":false,"modelsRepoIntegrationEnabled":false,"isMarketplaceEnabled":true},"codeViewBlobLayoutRoute.StyledBlob":{"rawLines":["# Источники данных для фронтенда","","## Purpose","Фиксация подтвержденных источников данных для фронтенд-контракта и правил агрегации.","","## Table of contents","- [Purpose](#purpose)","- [Что это за документ](#что-это-за-документ)","- [Важная оговорка по области применимости](#важная-оговорка-по-области-применимости)","- [Главный вывод](#главный-вывод)","- [Подтвержденные backend-источники для Grunw11](#подтвержденные-backend-источники-для-grunw11)","- [1. REST topology](#1-rest-topology)","","## Что это за документ","","Документ фиксирует, откуда именно backend может брать данные для контракта из [API_REFERENCE.md](API_REFERENCE.md), если ориентироваться на подтвержденные источники PrintSrv и на карту соответствий из внешнего артефакта `GRUNW11_SCADA_OBJECT_MAPPING.md` (файл не хранится в этом репозитории).","","Текущая база анализа:","","- автомат `Grunw11`","- frontend-контракт из [API_REFERENCE.md](API_REFERENCE.md)","- карта SCADA/PrintSrv-источников из внешнего артефакта `GRUNW11_SCADA_OBJECT_MAPPING.md` (вне текущего репозитория)","","Документ отвечает на вопрос: какие поля фронта можно брать напрямую, какие нужно агрегировать, а какие пока не подтверждены по данным Grunw11 и требуют отдельного решения в backend.","","## Важная оговорка по области применимости","","Этот документ нельзя читать как универсальную схему для всех автоматов.","","Он построен на детальном анализе именно `Grunw11`, а `Grunw11` в составе устройств заметно богаче многих других автоматов. По реестру автоматов уже видно, что набор устройств в проекте отличается от линии к линии:","","- есть автоматы с одним принтером, например `Hassia1`, `Hassia2`","- есть автоматы с двумя принтерами, например `Bosch`, `Grunw1`, `Hassia3`","- есть автоматы с четырьмя принтерами и расширенным набором камер, как `Grunw11`","- есть автоматы с другим типом камер вообще, например `trepko1`, `trepko2`","","Из этого следует главное правило интеграции:","","- количество камер и принтеров зависит от автомата","- список unit-объектов в PrintSrv зависит от автомата","- набор агрегированных полей в `scada` тоже зависит от автомата","","Например, если на линии нет второй камеры агрегации, то backend не должен ожидать наличие `CamAgregation2`, `Dev043...`, `Dev044...` и связанных с ними полей вроде `Dev043CounterGeneral` или `Dev044Error`. Аналогично, если на автомате только один принтер, то не будет ни `Printer12`, ни `LineDev012...`.","","Поэтому все примеры ниже надо читать так:","","- для `Grunw11` подтверждено именно в таком виде","- для других автоматов backend обязан сначала определить фактический состав устройств, а уже потом строить topology, `DEVICES_STATUS`, `ALERT` и другие ответы","","---","","## Главный вывод","","Для текущего минимального фронтового API источники распадаются на четыре группы:","","1. Статическая топология приложения.","2. Данные партии и очереди из `BatchQueue`.","3. Состояние линии из `Line`.","4. Диагностика камер и принтеров из `scada`, плюс прямые поля из `PrinterXX` и `Cam...`.","","Практическое правило для backend:","","- все, что относится к партии, брать в первую очередь из `BatchQueue`","- все, что относится к line-level состоянию, брать из `Line`","- все, что относится к агрегированной диагностике камер и принтеров, брать из `scada`","- прямые поля принтеров и камер брать из их unit-файлов только там, где `scada` не содержит нужной информации","","Но это правило работает только после шага discovery:","","- сначала backend определяет, какие устройства реально присутствуют у данного автомата","- затем проверяет, какие агрегированные поля реально существуют для этого состава устройств","- и только потом мапит их в frontend DTO","","---","","## Подтвержденные backend-источники для Grunw11","","Ниже перечислено то, что подтверждено именно для `Grunw11`. Для другого автомата этот список может быть короче, длиннее или содержать устройства других типов.","","### Реальные unit-источники","","- `BatchQueue`","- `Line`","- `scada`","- `Printer11`","- `Printer12`","- `Printer13`","- `Printer14`","- `CamAgregation1`","- `CamAgregation2`","- `CamAgregationBox1`","- `CamAgregationBox2`","- `CamChecker1`","- `CamChecker2`","- `CamEanChecker1`","- `CamEanChecker2`","- `CamEanChecker3`","- `CamEanChecker4`","","### Что делает `scada`","","Для `Grunw11` агрегатор `scada` генерирует для фронта подтвержденные диагностические теги:","","- `Dev041...`","- `Dev042...`","- `Dev043...`","- `Dev044...`","- `Dev071...`","- `Dev072...`","- `Dev073...`","- `Dev074...`","- `LineDev011...`","- `LineDev012...`","- `LineDev013...`","- `LineDev014...`","- `lineerr`","","Важно:","","- это не универсальный список полей `scada` для всех автоматов","- префиксы `Dev041...`, `Dev042...`, `Dev043...`, `Dev044...` появляются только там, где в конфигурации автомата реально есть соответствующие устройства и маппинг в `scada`","- то же самое относится к `LineDev011...`, `LineDev012...`, `LineDev013...`, `LineDev014...`: число этих групп зависит от числа принтеров на линии","","### Маппинг устройств в SCADA-диагностику","","Камеры:","","- `CamAgregation1 -\u003e Dev041`","- `CamAgregationBox1 -\u003e Dev042`","- `CamAgregation2 -\u003e Dev043`","- `CamAgregationBox2 -\u003e Dev044`","- `CamEanChecker1 -\u003e Dev071`","- `CamEanChecker2 -\u003e Dev072`","- `CamEanChecker3 -\u003e Dev073`","- `CamEanChecker4 -\u003e Dev074`","","Принтеры:","","- `Printer11 -\u003e LineDev011`","- `Printer12 -\u003e LineDev012`","- `Printer13 -\u003e LineDev013`","- `Printer14 -\u003e LineDev014`","","---","","## 1. REST topology","","### GET `/api/v1.0.0/workshops/topology`","","Источник:","","- не PrintSrv","- backend-конфигурация приложения","","Причина:","","- в анализе Grunw11 нет данных о цехах как о сущности верхнего уровня","- PrintSrv знает устройство и линию, но не прикладную мобильную иерархию цехов","","Рекомендация:","","- хранить `workshops` в YAML/БД backend","","Поля:","","| Поле API | Откуда брать |","|---|---|","| `id` | backend config |","| `name` | backend config |","| `totalUnits` | backend config или расчет по unit topology |","","### GET `/api/v1.0.0/workshops/{workshopId}/units/topology`","","Источник:","","- не PrintSrv напрямую","- backend-конфигурация приложения","","Поля:","","| Поле API | Откуда брать |","|---|---|","| `id` | backend config |","| `workshopId` | backend config |","| `unit` | backend config |","","### GET `/api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology`","","Источник:","","- `Line___Unit0.xml`","- группировка по именам устройств","","Для Grunw11 подтверждено:","","- `Level1Printers = Printer11,Printer12,Printer13,Printer14`","- `LineDevices = CamChecker1,CamChecker2,CamAgregation1,CamAgregation2,CamAgregationBox1,CamAgregationBox2,CamEanChecker1,CamEanChecker2,CamEanChecker3,CamEanChecker4,Printer11,Printer12,Printer13,Printer14`","","Рекомендуемая сборка ответа для Grunw11:","","| Поле API | Источник |","|---|---|","| `devices.printers` | `Line.Level1Printers` |","| `devices.aggregationCams` | фильтр `Line.LineDevices` по `CamAgregation1`, `CamAgregation2` |","| `devices.aggregationBoxCams` | фильтр `Line.LineDevices` по `CamAgregationBox1`, `CamAgregationBox2` |","| `devices.checkerCams` | фильтр `Line.LineDevices` по `CamChecker1`, `CamChecker2`, `CamEanChecker1..4` |","","Примечание:","","- `CamEanChecker1..4` не имеют отдельной группы в текущем контракте frontend и логично попадают в `checkerCams`","- для другого аппарата backend не должен хардкодить этот набор; его нужно собирать из фактического `Line.LineDevices` и связанных unit-конфигов","- актуальный контракт дополнен per-unit раскладкой устройств: `groups` (группы «машина»/«Поток N») и `deviceMeta` (имя на экране + признак счётчиков) настраиваются в админке per-unit (`unit_devices`: `display_name`, `group_label`, `display_order`, `show_counters`, `scada_prefix`). Основание и дефолты групп — [PRINTSRV_UI_MAP.md](PRINTSRV_UI_MAP.md)","","---","","## 2. WebSocket `/ws/unit/{unitId}`","","## `LINE_STATUS`","","Источник для каждого поля:","","| Поле API | Рекомендуемый источник | Комментарий |","|---|---|---|","| `lineName` | backend topology config | Лучше брать отображаемое имя аппарата, а не имя `.mpr` файла |","| `lineState` | `Line.ST` | Подтвержденный источник |","| `shortCode` | `BatchQueue.kmc`, fallback `Printer11.kmc` | В текущей документации это наиболее близкий подтвержденный источник короткого кода продукта |","| `description` | `BatchQueue.description`, fallback `Printer11.descr` | Для линии первичнее `BatchQueue`, т.к. это текущая партия |","| `ean13` | `BatchQueue.ean13`, fallback `Printer11.ean13` | Аналогично |","| `batchNumber` | `BatchQueue.batch`, fallback `Printer11.partynumber` | `BatchQueue.batch` лучше отражает текущую партию линии |","| `dateProduced` | `BatchQueue.dateproduced`, fallback `Printer11.dateproduced` | Подтверждено |","| `datePacking` | `BatchQueue.datepack`, fallback `Printer11.datepack` | Подтверждено |","| `dateExpiration` | `BatchQueue.dateexpiration`, fallback `Printer11.dateexpiration` | Подтверждено |","| `initialCounter` | требует уточнения, временно `Printer11.curitem` с парсингом первого сегмента | В Grunw11 нет отдельного подтвержденного поля именно начального счетчика; `BatchQueue.begincounter` это флаг, а не само числовое значение |","| `site` | `BatchQueue.place`, fallback `Printer11.place` | Семантически это место/слот на линии |","| `itf` | `BatchQueue.itf`, fallback `Printer11.itf` | Подтверждено |","| `capacity` | `BatchQueue.emk`, fallback `Printer11.emk` | В документации подтверждено как количество единиц в коробе |","| `boxCount` | требует бизнес-уточнения, кандидаты `BatchQueue.kole` или `BatchQueue.kolm` | По подтвержденной документации `kole` = количество единиц, `kolm` = количество маркировок; название `boxCount` не совпадает с семантикой 1:1 |","| `packageCount` | требует бизнес-уточнения, кандидаты `BatchQueue.kolm` или `BatchQueue.kole` | Аналогично |","| `freeze` | `BatchQueue.frozen` | Подтверждено |","| `region` | `BatchQueue.region` | Подтверждено |","| `design` | `BatchQueue.designe`, fallback `Printer11.designe` | Подтверждено |","| `printDM` | `BatchQueue.printdm`, fallback `Printer11.printdm` | Подтверждено |","","Итог по `LINE_STATUS`:","","- почти весь блок можно собрать из `BatchQueue` + `Line`","- `Printer11` нужен как fallback или как источник отдельных принтерных деталей","- поля `initialCounter`, `boxCount`, `packageCount` сейчас семантически неоднозначны и должны быть подтверждены отдельно","- наличие fallback-полей в `Printer11` тоже зависит от аппарата: на некоторых линиях принтеров может быть больше одного, а на некоторых только один","","---","","## `DEVICES_STATUS`","","Это самый аппаратозависимый блок во всем контракте. Здесь нельзя предполагать фиксированное количество устройств.","","Для одного аппарата массив `printers[]` может содержать один объект, для другого два, для третьего четыре и более. То же касается камер: где-то будут только `CamAgregation` и `CamChecker`, где-то добавятся `CamEanChecker1..4`, а где-то набор устройств будет вообще другой.","","Следствие для backend:","","- размеры массивов `printers`, `aggregationCams`, `aggregationBoxCams`, `checkerCams` должны определяться по topology конкретного аппарата","- список полей `scada`, доступных для этих массивов, должен выводиться из фактического device-to-scada mapping, а не из предположения, что всегда существуют `Dev041...Dev044` или `LineDev011...LineDev014`","","### Принтеры","","Рекомендуемая схема:","","| Поле API | Источник | Комментарий |","|---|---|---|","| `printers[].deviceName` | topology | `Printer11..14` |","| `printers[].state` | `PrinterXX.ST` | Прямое состояние принтера |","| `printers[].error` | `scada.LineDev0XXError` | Это именно тот error-слой, который видит SCADA |","| `printers[].batch` | если нужен счетчик: `PrinterXX.kolm`; если нужна строка как в SCADA: `PrinterXX.curitem` | В текущем фронтовом описании поле названо как счетчик, но в SCADA карточка принтера использует `curitem` |","","Практическая рекомендация:","","- если фронту нужен именно числовой счетчик, лучше отдать `PrinterXX.kolm`","- если фронту нужно то же, что в SCADA-блоке принтера, лучше переименовать поле и отдавать `PrinterXX.curitem`","- количество принтеров на линии брать не из имени `Printer11`, а из topology аппарата; на другой линии список может закончиться на `Printer11`, `Printer12` или иметь иной состав","","### Камеры агрегации на потоке","","Для Grunw11 это:","","- `CamAgregation1`","- `CamAgregation2`","","Рекомендуемая схема:","","| Поле API | Источник | Пример для Grunw11 |","|---|---|---|","| `aggregationCams[].deviceName` | topology | `CamAgregation1`, `CamAgregation2` |","| `aggregationCams[].read` | `scada.Dev041CounterGeneral`, `scada.Dev043CounterGeneral` | агрегированная статистика SCADA |","| `aggregationCams[].unread` | `scada.Dev041CounterMissing`, `scada.Dev043CounterMissing` | агрегированная статистика SCADA |","| `aggregationCams[].state` | `scada.Dev041Work`, `scada.Dev043Work`, fallback direct `Cam...ST` | для UI ближе именно `Work`-флаг из `scada` |","| `aggregationCams[].error` | `scada.Dev041Error`, `scada.Dev043Error` | агрегированная ошибка |","","Важно:","","- это пример именно для `Grunw11`","- на аппарате с одной камерой агрегации будет только одна группа полей такого типа","- backend должен строить соответствие не по номерам `041` и `043`, а по реальному маппингу устройства в `scada`","","### Камеры агрегации на коробе","","Для Grunw11 это:","","- `CamAgregationBox1`","- `CamAgregationBox2`","","Рекомендуемая схема:","","| Поле API | Источник | Пример для Grunw11 |","|---|---|---|","| `aggregationBoxCams[].deviceName` | topology | `CamAgregationBox1`, `CamAgregationBox2` |","| `aggregationBoxCams[].read` | `scada.Dev042CounterGeneral`, `scada.Dev044CounterGeneral` | |","| `aggregationBoxCams[].unread` | `scada.Dev042CounterMissing`, `scada.Dev044CounterMissing` | |","| `aggregationBoxCams[].state` | `scada.Dev042Work`, `scada.Dev044Work`, fallback direct `Cam...ST` | |","| `aggregationBoxCams[].error` | `scada.Dev042Error`, `scada.Dev044Error` | |","","Важно:","","- на других аппаратах камеры коробочной агрегации могут отсутствовать совсем","- соответственно, могут отсутствовать и группы полей `Dev042...` или `Dev044...`","","### Checker-камеры","","Для Grunw11 в `checkerCams` логично включать:","","- `CamChecker1`","- `CamChecker2`","- `CamEanChecker1`","- `CamEanChecker2`","- `CamEanChecker3`","- `CamEanChecker4`","","Но источник данных здесь неоднородный.","","И это еще один аппаратозависимый участок:","","- на части аппаратов есть только `CamChecker`","- на части есть и `CamChecker`, и `CamEanChecker1..4`","- на части аппаратов может не быть `CamEanChecker` вообще","","#### EAN-checker камеры","","Для `CamEanChecker1..4` использовать тот же подход, что и для SCADA:","","| Поле API | Источник |","|---|---|","| `deviceName` | topology |","| `read` | `scada.Dev071..074CounterGeneral` |","| `unread` | `scada.Dev071..074CounterMissing` |","| `state` | `scada.Dev071..074Work` |","| `error` | `scada.Dev071..074Error` |","","Но только если такие устройства вообще существуют на данном аппарате. Для аппаратов без `CamEanChecker1..4` этих полей в `scada` тоже не будет.","","#### CamChecker1 и CamChecker2","","Для `CamChecker1/2` отдельного `Dev0XX`-слоя в `scada` для Grunw11 не подтверждено.","","Подтвержденные прямые поля в самих unit-файлах:","","- `ST`","- `BatchSucceeded`","- `BatchFailed`","- `message`","- `LastReadTime`","","Рекомендуемая схема:","","| Поле API | Источник | Комментарий |","|---|---|---|","| `deviceName` | topology | |","| `read` | `CamCheckerX.BatchSucceeded` | прямой device-source |","| `unread` | `CamCheckerX.BatchFailed` | прямой device-source |","| `state` | `CamCheckerX.ST` | прямой device-source |","| `error` | backend-вычисление из runtime `err` или из alert-service | отдельного подтвержденного `scada.Dev...Error` для CamChecker1/2 нет |","","Итог по `DEVICES_STATUS`:","","- для принтеров и камер с Dev-мэппингом основным источником диагностики должен быть `scada`","- для `CamChecker1/2` backend должен брать прямые счетчики из device-unit и сам вычислять error-флаг","- количество объектов в каждом массиве и набор доступных `scada`-полей должны вычисляться динамически по конкретному аппарату","","---","","## `QUEUE`","","Источник:","","- `BatchQueue`","- при возможности лучше опираться на структурированное представление очереди, а не только на display-строки","","Подтвержденные поля:","","- `Item01..Item10`","- `Item01Selected..Item10Selected`","- `CurItem`","","Дополнительный источник, если backend умеет читать его отдельно:","","- `Config/Default/Files/BatchQueue.json`","","Рекомендуемая схема:","","| Поле API | Источник | Комментарий |","|---|---|---|","| `items[].position` | индекс `Item01..Item10` | подтверждено |","| `items[].shortCode` | предпочтительно структурированный парсинг из `BatchQueue.json`; fallback парсинг `ItemXX` | `ItemXX` это display-строка, формат может быть нестрогим |","| `items[].batch` | предпочтительно `BatchQueue.json`; fallback парсинг `ItemXX` | |","| `items[].dateProduced` | предпочтительно `BatchQueue.json`; fallback парсинг `ItemXX` | |","","Практическая рекомендация:","","- если backend имеет доступ к `BatchQueue.json`, это более надежный источник для `QUEUE`, чем парсинг строк `Item01..Item10`","","---","","## `ERRORS`","","Этот блок не собирается из одного XML-файла.","","Он требует backend-агрегации из нескольких слоев:","","1. runtime-состояние устройств","2. `scada`-теги ошибок","3. состояние линии","4. журнал событий","","### `deviceErrors[]`","","Рекомендуемые источники:","","| Поле API | Откуда брать | Комментарий |","|---|---|---|","| `objectName` | реальное device-name: `Printer11`, `CamAgregation1`, `Line` и т.д. | лучше использовать backend-имя устройства, не SCADA-имя виджета |","| `propertyDesc` | backend mapping table по флагу ошибки | например `LineDev011Error`, `Dev041Dublicate`, `Dev071Fail` |","| `value` | текущее значение error-флага | обычно `1`/`0` |","| `description` | backend human-readable message | из словаря ошибок или описания состояния |","","Для Grunw11 подтвержденные error-источники:","","- `scada.LineDev011Error..LineDev014Error`","- `scada.Dev041Error..Dev044Error`","- `scada.Dev071Error..Dev074Error`","- `Line.Error`","- `Line.ErrorMessage`","","Для других аппаратов этот список должен пересчитываться по фактическому составу устройств. Нельзя считать, что если в коде для `Grunw11` есть `Dev041Error` или `LineDev014Error`, то такие же поля будут у любой другой линии.","","Для `CamChecker1/2` error-флаг нужно вычислять backend-ом из runtime-состояния камеры, так как отдельного `Dev`-агрегата не подтверждено.","","### `logs[]`","","По анализу Grunw11 и существующей документации наиболее реалистичные источники:","","- `sqlite_logs.db`, если backend использует его как журнал событий","- либо файлы логов PrintSrv, если backend читает их напрямую","","На уровне минимального фронтового контракта это backend-агрегируемый слой, а не поле из одного unit.","","---","","## 3. WebSocket `/ws/live`","","## `UNITS_STATUS`","","### `payload[].event`","","Не существует как готовое поле в Grunw11 unit-файлах.","","Это backend-производное представление состояния аппарата.","","Рекомендуемые входы для вычисления:","","- `Line.ST`","- `Line.Error`","- `Line.ErrorMessage`","- `scada.lineerr`","","Пример логики:","","- если есть ошибка линии или активные device-ошибки, отдавать текст ошибки","- если `Line.ST = 1`, отдавать состояние типа `В работе`","- если `Line.ST != 1` и ошибок нет, отдавать `Стоп` или аналогичный warning-state","","### `payload[].timer`","","Для Grunw11 в проанализированных PrintSrv-данных подтвержденного прямого поля таймера состояния не найдено.","","Рекомендация:","","- вычислять backend-ом как время с момента последней смены вычисленного `event`","","---","","## `ALERT_SNAPSHOT` и `ALERT`","","Это не прямой снимок одного устройства, а результат backend-агрегации.","","### Какие поля можно брать","","| Поле API | Источник |","|---|---|","| `workshopId` | backend topology config |","| `unitId` | backend topology config |","| `unitName` | backend topology config |","| `active` | backend alert engine |","| `timestamp` | backend alert engine |","","### Из чего собирать сами алерты","","Для Grunw11 подтвержденные источники для alert engine:","","- `Line.Error`","- `Line.ErrorMessage`","- `Line.ST`","- `scada.lineerr`","- `scada.LineDev011Error..014Error`","- `scada.Dev041Error..044Error`","- `scada.Dev071Error..074Error`","- runtime `err` для устройств без `scada`-обертки, например `CamChecker1/2`","","Для других аппаратов backend должен собирать список alert-источников по фактическим unit-объектам и реально существующим `scada`-полям, а не по фиксированному шаблону `Grunw11`.","","### `errors[].device`","","Рекомендуется отдавать реальные backend-имена устройств:","","- `Printer11`","- `Printer12`","- `CamAgregation1`","- `CamEanChecker1`","- `Line`","","Не рекомендуется отдавать:","","- `ConnectionPrinter11`","- `CMSDev041`","- `cdev41err`","","Причина:","","- это экранные объекты SCADA, а не реальные backend-устройства","","### `errors[].code`","","Источник:","","- `Line.Error` или device-specific `message`, если есть код ошибки","- если сообщение warning-класса и числового кода нет, можно отдавать `0`","","### `errors[].message`","","Источник:","","- `Line.ErrorMessage`, если ошибка относится к линии","- backend-словарь интерпретации кодов device/message-флагов","- fallback: человекочитаемое описание сформированного alert-condition","","### `severity`","","Это backend-вычисляемое поле.","","Под текущую модель Grunw11 логично:","","- `Critical`, если есть активная ошибка линии или устройства","- `Warning`, если линия не работает, но явной ошибки нет","","---","","## 4. Что можно брать уже сейчас без спорных мест","","Ниже список полей, для которых источник по Grunw11 подтвержден достаточно уверенно.","","### Можно забирать сразу","","Ниже список полей, которые для `Grunw11` подтверждены уверенно. Этот блок не означает, что те же самые поля обязательно существуют у любого другого аппарата в том же объеме.","","- `lineState -\u003e Line.ST`","- `description -\u003e BatchQueue.description`","- `ean13 -\u003e BatchQueue.ean13`","- `batchNumber -\u003e BatchQueue.batch`","- `dateProduced -\u003e BatchQueue.dateproduced`","- `datePacking -\u003e BatchQueue.datepack`","- `dateExpiration -\u003e BatchQueue.dateexpiration`","- `site -\u003e BatchQueue.place`","- `itf -\u003e BatchQueue.itf`","- `capacity -\u003e BatchQueue.emk`","- `freeze -\u003e BatchQueue.frozen`","- `region -\u003e BatchQueue.region`","- `design -\u003e BatchQueue.designe`","- `printDM -\u003e BatchQueue.printdm`","- `printers[].state -\u003e PrinterXX.ST`","- `printers[].error -\u003e scada.LineDev0XXError`","- `aggregationCams[].read/unread/error -\u003e scada.Dev041/043...`","- `aggregationBoxCams[].read/unread/error -\u003e scada.Dev042/044...`","- `ean-checker state/read/unread/error -\u003e scada.Dev071..074...`","","### Требуют отдельного согласования","","- `initialCounter`","- `boxCount`","- `packageCount`","- `UNITS_STATUS.payload[].event`","- `UNITS_STATUS.payload[].timer`","- `CamChecker1/2.error`","- структура `QUEUE.items[]`, если не читать `BatchQueue.json`","","---","","## Итоговая рекомендация для backend","","Для минимального frontend-контракта backend должен строить ответ из следующих базовых источников:","","1. topology config приложения","2. `BatchQueue`","3. `Line`","4. `scada`","5. `Printer11..14`","6. `CamAgregation1..2`","7. `CamAgregationBox1..2`","8. `CamChecker1..2`","9. `CamEanChecker1..4`","","Но это перечень для `Grunw11`, а не для всех аппаратов. В общем случае правильный порядок работы такой:","","1. определить состав устройств конкретного аппарата","2. определить, какие unit-объекты реально существуют в PrintSrv для этого аппарата","3. определить, какие агрегированные поля реально публикует `scada` для этого состава устройств","4. только после этого собирать frontend DTO","","И отдельно должен иметь собственный слой агрегации для:","","1. `ALERT` и `ALERT_SNAPSHOT`","2. `UNITS_STATUS.event`","3. `UNITS_STATUS.timer`","4. `ERRORS.deviceErrors[]`","5. `QUEUE.items[]`, если нужна не display-строка, а структурированная очередь","","---","","## Короткий практический вывод","","Если смотреть только на то, что подтверждено по Grunw11, то frontend сейчас лучше всего кормить так:","","- карточку партии собирать в первую очередь из `BatchQueue`","- диагностику устройств брать из `scada`","- прямые статусы принтеров брать из `PrinterXX`","- прямые статусы `CamChecker1/2`, где нет `Dev`-обертки, брать из самих camera-unit","- все человекочитаемые event/alert/log представления вычислять в backend, а не пытаться читать как готовые поля PrintSrv","","И главное уточнение: это не означает, что на каждом аппарате обязательно будут все эти сущности и все эти поля. У одного аппарата может быть один принтер и одна камера, у другого два принтера и четыре EAN-checker камеры, у третьего иной состав устройств. Поэтому backend должен опираться на discovery конкретного аппарата, а не на фиксированный список полей из `Grunw11`."],"stylingDirectives":[[[0,32,"pl-mh"],[2,32,"pl-en"]],[],[[0,10,"pl-mh"],[3,10,"pl-en"]],[],[],[[0,20,"pl-mh"],[3,20,"pl-en"]],[[0,1,"pl-v"],[2,3,"pl-s"],[10,11,"pl-s"],[11,12,"pl-s"],[12,20,"pl-corl"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[22,23,"pl-s"],[23,24,"pl-s"],[24,44,"pl-corl"],[44,45,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[42,43,"pl-s"],[43,44,"pl-s"],[44,84,"pl-corl"],[84,85,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[16,17,"pl-s"],[17,18,"pl-s"],[18,32,"pl-corl"],[32,33,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[47,48,"pl-s"],[48,49,"pl-s"],[49,94,"pl-corl"],[94,95,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[19,20,"pl-s"],[20,21,"pl-s"],[21,37,"pl-corl"],[37,38,"pl-s"]],[],[[0,22,"pl-mh"],[3,22,"pl-en"]],[],[[78,79,"pl-s"],[95,96,"pl-s"],[96,97,"pl-s"],[97,113,"pl-corl"],[113,114,"pl-s"],[220,221,"pl-s"],[221,252,"pl-c1"],[252,253,"pl-s"]],[],[],[],[[0,1,"pl-v"],[10,11,"pl-s"],[11,18,"pl-c1"],[18,19,"pl-s"]],[[0,1,"pl-v"],[23,24,"pl-s"],[40,41,"pl-s"],[41,42,"pl-s"],[42,58,"pl-corl"],[58,59,"pl-s"]],[[0,1,"pl-v"],[56,57,"pl-s"],[57,88,"pl-c1"],[88,89,"pl-s"]],[],[],[],[[0,42,"pl-mh"],[3,42,"pl-en"]],[],[],[],[[40,41,"pl-s"],[41,48,"pl-c1"],[48,49,"pl-s"],[53,54,"pl-s"],[54,61,"pl-c1"],[61,62,"pl-s"]],[],[[0,1,"pl-v"],[44,45,"pl-s"],[45,52,"pl-c1"],[52,53,"pl-s"],[55,56,"pl-s"],[56,63,"pl-c1"],[63,64,"pl-s"]],[[0,1,"pl-v"],[45,46,"pl-s"],[46,51,"pl-c1"],[51,52,"pl-s"],[54,55,"pl-s"],[55,61,"pl-c1"],[61,62,"pl-s"],[64,65,"pl-s"],[65,72,"pl-c1"],[72,73,"pl-s"]],[[0,1,"pl-v"],[71,72,"pl-s"],[72,79,"pl-c1"],[79,80,"pl-s"]],[[0,1,"pl-v"],[54,55,"pl-s"],[55,62,"pl-c1"],[62,63,"pl-s"],[65,66,"pl-s"],[66,73,"pl-c1"],[73,74,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"],[31,32,"pl-s"],[32,37,"pl-c1"],[37,38,"pl-s"]],[],[[90,91,"pl-s"],[91,105,"pl-c1"],[105,106,"pl-s"],[108,109,"pl-s"],[109,118,"pl-c1"],[118,119,"pl-s"],[121,122,"pl-s"],[122,131,"pl-c1"],[131,132,"pl-s"],[164,165,"pl-s"],[165,185,"pl-c1"],[185,186,"pl-s"],[191,192,"pl-s"],[192,203,"pl-c1"],[203,204,"pl-s"],[271,272,"pl-s"],[272,281,"pl-c1"],[281,282,"pl-s"],[287,288,"pl-s"],[288,301,"pl-c1"],[301,302,"pl-s"]],[],[],[],[[0,1,"pl-v"],[6,7,"pl-s"],[7,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[117,118,"pl-s"],[118,132,"pl-c1"],[132,133,"pl-s"],[135,136,"pl-s"],[136,141,"pl-c1"],[141,142,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,16,"pl-mh"],[3,16,"pl-en"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[30,31,"pl-s"],[31,41,"pl-c1"],[41,42,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[22,23,"pl-s"],[23,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[36,37,"pl-s"],[37,42,"pl-c1"],[42,43,"pl-s"],[65,66,"pl-s"],[66,75,"pl-c1"],[75,76,"pl-s"],[79,80,"pl-s"],[80,86,"pl-c1"],[86,87,"pl-s"]],[],[],[],[[0,1,"pl-v"],[57,58,"pl-s"],[58,68,"pl-c1"],[68,69,"pl-s"]],[[0,1,"pl-v"],[54,55,"pl-s"],[55,59,"pl-c1"],[59,60,"pl-s"]],[[0,1,"pl-v"],[78,79,"pl-s"],[79,84,"pl-c1"],[84,85,"pl-s"]],[[0,1,"pl-v"],[72,73,"pl-s"],[73,78,"pl-c1"],[78,79,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[[0,3,"pl-ms"]],[],[[0,47,"pl-mh"],[3,47,"pl-en"]],[],[[49,50,"pl-s"],[50,57,"pl-c1"],[57,58,"pl-s"]],[],[[0,27,"pl-mh"],[4,27,"pl-en"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[],[[0,22,"pl-mh"],[4,22,"pl-en"],[15,16,"pl-s"],[16,21,"pl-c1"],[21,22,"pl-s"]],[],[[4,5,"pl-s"],[5,12,"pl-c1"],[12,13,"pl-s"],[24,25,"pl-s"],[25,30,"pl-c1"],[30,31,"pl-s"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[],[],[],[[0,1,"pl-v"],[36,37,"pl-s"],[37,42,"pl-c1"],[42,43,"pl-s"]],[[0,1,"pl-v"],[11,12,"pl-s"],[12,21,"pl-c1"],[21,22,"pl-s"],[24,25,"pl-s"],[25,34,"pl-c1"],[34,35,"pl-s"],[37,38,"pl-s"],[38,47,"pl-c1"],[47,48,"pl-s"],[50,51,"pl-s"],[51,60,"pl-c1"],[60,61,"pl-s"],[165,166,"pl-s"],[166,171,"pl-c1"],[171,172,"pl-s"]],[[0,1,"pl-v"],[26,27,"pl-s"],[27,40,"pl-c1"],[40,41,"pl-s"],[43,44,"pl-s"],[44,57,"pl-c1"],[57,58,"pl-s"],[60,61,"pl-s"],[61,74,"pl-c1"],[74,75,"pl-s"],[77,78,"pl-s"],[78,91,"pl-c1"],[91,92,"pl-s"]],[],[[0,41,"pl-mh"],[4,41,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,19,"pl-mh"],[3,19,"pl-en"]],[],[[0,40,"pl-mh"],[4,40,"pl-en"],[8,9,"pl-s"],[9,39,"pl-c1"],[39,40,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[],[],[[0,1,"pl-v"],[10,11,"pl-s"],[11,20,"pl-c1"],[20,21,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[26,27,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,5,"pl-c1"],[5,6,"pl-s"],[7,8,"pl-ml"],[24,25,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"],[9,10,"pl-ml"],[26,27,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[60,61,"pl-ml"]],[],[[0,59,"pl-mh"],[4,59,"pl-en"],[8,9,"pl-s"],[9,58,"pl-c1"],[58,59,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[26,27,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,5,"pl-c1"],[5,6,"pl-s"],[7,8,"pl-ml"],[24,25,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[32,33,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"],[9,10,"pl-ml"],[26,27,"pl-ml"]],[],[[0,76,"pl-mh"],[4,76,"pl-en"],[8,9,"pl-s"],[9,75,"pl-c1"],[75,76,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"]],[[0,1,"pl-v"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,59,"pl-c1"],[59,60,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,206,"pl-c1"],[206,207,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[23,24,"pl-s"],[24,43,"pl-c1"],[43,44,"pl-s"],[45,46,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"],[28,29,"pl-ml"],[37,38,"pl-s"],[38,54,"pl-c1"],[54,55,"pl-s"],[59,60,"pl-s"],[60,74,"pl-c1"],[74,75,"pl-s"],[77,78,"pl-s"],[78,92,"pl-c1"],[92,93,"pl-s"],[94,95,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,29,"pl-c1"],[29,30,"pl-s"],[31,32,"pl-ml"],[40,41,"pl-s"],[41,57,"pl-c1"],[57,58,"pl-s"],[62,63,"pl-s"],[63,80,"pl-c1"],[80,81,"pl-s"],[83,84,"pl-s"],[84,101,"pl-c1"],[101,102,"pl-s"],[103,104,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,22,"pl-c1"],[22,23,"pl-s"],[24,25,"pl-ml"],[33,34,"pl-s"],[34,50,"pl-c1"],[50,51,"pl-s"],[55,56,"pl-s"],[56,67,"pl-c1"],[67,68,"pl-s"],[70,71,"pl-s"],[71,82,"pl-c1"],[82,83,"pl-s"],[85,86,"pl-s"],[86,103,"pl-c1"],[103,104,"pl-s"],[105,106,"pl-ml"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"],[98,99,"pl-s"],[99,110,"pl-c1"],[110,111,"pl-s"]],[[0,1,"pl-v"],[99,100,"pl-s"],[100,116,"pl-c1"],[116,117,"pl-s"]],[[0,1,"pl-v"],[62,63,"pl-s"],[63,69,"pl-c1"],[69,70,"pl-s"],[101,102,"pl-s"],[102,112,"pl-c1"],[112,113,"pl-s"],[184,185,"pl-s"],[185,197,"pl-c1"],[197,198,"pl-s"],[200,201,"pl-s"],[201,213,"pl-c1"],[213,214,"pl-s"],[216,217,"pl-s"],[217,228,"pl-c1"],[228,229,"pl-s"],[231,232,"pl-s"],[232,245,"pl-c1"],[245,246,"pl-s"],[248,249,"pl-s"],[249,262,"pl-c1"],[262,263,"pl-s"],[265,266,"pl-s"],[266,278,"pl-c1"],[278,279,"pl-s"],[310,311,"pl-s"],[329,330,"pl-s"],[330,331,"pl-s"],[331,349,"pl-corl"],[349,350,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,35,"pl-mh"],[3,35,"pl-en"],[16,17,"pl-s"],[17,34,"pl-c1"],[34,35,"pl-s"]],[],[[0,16,"pl-mh"],[3,16,"pl-en"],[3,4,"pl-s"],[4,15,"pl-c1"],[15,16,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[36,37,"pl-ml"],[50,51,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"],[13,14,"pl-ml"],[39,40,"pl-ml"],[89,90,"pl-s"],[90,94,"pl-c1"],[94,95,"pl-s"],[102,103,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"],[14,15,"pl-ml"],[16,17,"pl-s"],[17,24,"pl-c1"],[24,25,"pl-s"],[26,27,"pl-ml"],[52,53,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"],[14,15,"pl-ml"],[16,17,"pl-s"],[17,31,"pl-c1"],[31,32,"pl-s"],[43,44,"pl-s"],[44,57,"pl-c1"],[57,58,"pl-s"],[59,60,"pl-ml"],[153,154,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"],[16,17,"pl-ml"],[18,19,"pl-s"],[19,41,"pl-c1"],[41,42,"pl-s"],[53,54,"pl-s"],[54,69,"pl-c1"],[69,70,"pl-s"],[71,72,"pl-ml"],[93,94,"pl-s"],[94,104,"pl-c1"],[104,105,"pl-s"],[131,132,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[12,13,"pl-s"],[13,29,"pl-c1"],[29,30,"pl-s"],[41,42,"pl-s"],[42,57,"pl-c1"],[57,58,"pl-s"],[59,60,"pl-ml"],[72,73,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"],[16,17,"pl-ml"],[18,19,"pl-s"],[19,35,"pl-c1"],[35,36,"pl-s"],[47,48,"pl-s"],[48,69,"pl-c1"],[69,70,"pl-s"],[71,72,"pl-ml"],[73,74,"pl-s"],[74,90,"pl-c1"],[90,91,"pl-s"],[128,129,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,15,"pl-c1"],[15,16,"pl-s"],[17,18,"pl-ml"],[19,20,"pl-s"],[20,43,"pl-c1"],[43,44,"pl-s"],[55,56,"pl-s"],[56,78,"pl-c1"],[78,79,"pl-s"],[80,81,"pl-ml"],[95,96,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"],[16,17,"pl-ml"],[18,19,"pl-s"],[19,38,"pl-c1"],[38,39,"pl-s"],[50,51,"pl-s"],[51,69,"pl-c1"],[69,70,"pl-s"],[71,72,"pl-ml"],[86,87,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"],[19,20,"pl-ml"],[21,22,"pl-s"],[22,47,"pl-c1"],[47,48,"pl-s"],[59,60,"pl-s"],[60,84,"pl-c1"],[84,85,"pl-s"],[86,87,"pl-ml"],[101,102,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"],[19,20,"pl-ml"],[49,50,"pl-s"],[50,67,"pl-c1"],[67,68,"pl-s"],[98,99,"pl-ml"],[174,175,"pl-s"],[175,198,"pl-c1"],[198,199,"pl-s"],[238,239,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"],[9,10,"pl-ml"],[11,12,"pl-s"],[12,28,"pl-c1"],[28,29,"pl-s"],[40,41,"pl-s"],[41,56,"pl-c1"],[56,57,"pl-s"],[58,59,"pl-ml"],[97,98,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,6,"pl-c1"],[6,7,"pl-s"],[8,9,"pl-ml"],[10,11,"pl-s"],[11,25,"pl-c1"],[25,26,"pl-s"],[37,38,"pl-s"],[38,51,"pl-c1"],[51,52,"pl-s"],[53,54,"pl-ml"],[68,69,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"],[13,14,"pl-ml"],[15,16,"pl-s"],[16,30,"pl-c1"],[30,31,"pl-s"],[42,43,"pl-s"],[43,56,"pl-c1"],[56,57,"pl-s"],[58,59,"pl-ml"],[119,120,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"],[13,14,"pl-ml"],[51,52,"pl-s"],[52,67,"pl-c1"],[67,68,"pl-s"],[73,74,"pl-s"],[74,89,"pl-c1"],[89,90,"pl-s"],[91,92,"pl-ml"],[124,125,"pl-s"],[125,129,"pl-c1"],[129,130,"pl-s"],[152,153,"pl-s"],[153,157,"pl-c1"],[157,158,"pl-s"],[193,194,"pl-s"],[194,202,"pl-c1"],[202,203,"pl-s"],[234,235,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,15,"pl-c1"],[15,16,"pl-s"],[17,18,"pl-ml"],[55,56,"pl-s"],[56,71,"pl-c1"],[71,72,"pl-s"],[77,78,"pl-s"],[78,93,"pl-c1"],[93,94,"pl-s"],[95,96,"pl-ml"],[108,109,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[13,14,"pl-s"],[14,31,"pl-c1"],[31,32,"pl-s"],[33,34,"pl-ml"],[48,49,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[13,14,"pl-s"],[14,31,"pl-c1"],[31,32,"pl-s"],[33,34,"pl-ml"],[48,49,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[13,14,"pl-s"],[14,32,"pl-c1"],[32,33,"pl-s"],[44,45,"pl-s"],[45,62,"pl-c1"],[62,63,"pl-s"],[64,65,"pl-ml"],[79,80,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"],[12,13,"pl-ml"],[14,15,"pl-s"],[15,33,"pl-c1"],[33,34,"pl-s"],[45,46,"pl-s"],[46,63,"pl-c1"],[63,64,"pl-s"],[65,66,"pl-ml"],[80,81,"pl-ml"]],[],[[8,9,"pl-s"],[9,20,"pl-c1"],[20,21,"pl-s"]],[],[[0,1,"pl-v"],[35,36,"pl-s"],[36,46,"pl-c1"],[46,47,"pl-s"],[50,51,"pl-s"],[51,55,"pl-c1"],[55,56,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[7,8,"pl-s"],[8,22,"pl-c1"],[22,23,"pl-s"],[25,26,"pl-s"],[26,34,"pl-c1"],[34,35,"pl-s"],[37,38,"pl-s"],[38,50,"pl-c1"],[50,51,"pl-s"]],[[0,1,"pl-v"],[27,28,"pl-s"],[28,37,"pl-c1"],[37,38,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,19,"pl-mh"],[3,19,"pl-en"],[3,4,"pl-s"],[4,18,"pl-c1"],[18,19,"pl-s"]],[],[],[],[[27,28,"pl-s"],[28,38,"pl-c1"],[38,39,"pl-s"],[157,158,"pl-s"],[158,171,"pl-c1"],[171,172,"pl-s"],[175,176,"pl-s"],[176,186,"pl-c1"],[186,187,"pl-s"],[206,207,"pl-s"],[207,224,"pl-c1"],[224,225,"pl-s"]],[],[],[],[[0,1,"pl-v"],[19,20,"pl-s"],[20,28,"pl-c1"],[28,29,"pl-s"],[31,32,"pl-s"],[32,47,"pl-c1"],[47,48,"pl-s"],[50,51,"pl-s"],[51,69,"pl-c1"],[69,70,"pl-s"],[72,73,"pl-s"],[73,84,"pl-c1"],[84,85,"pl-s"]],[[0,1,"pl-v"],[15,16,"pl-s"],[16,21,"pl-c1"],[21,22,"pl-s"],[157,158,"pl-s"],[158,173,"pl-c1"],[173,174,"pl-s"],[179,180,"pl-s"],[180,203,"pl-c1"],[203,204,"pl-s"]],[],[[0,12,"pl-mh"],[4,12,"pl-en"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"],[36,37,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,24,"pl-c1"],[24,25,"pl-s"],[26,27,"pl-ml"],[37,38,"pl-ml"],[39,40,"pl-s"],[40,53,"pl-c1"],[53,54,"pl-s"],[55,56,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[23,24,"pl-s"],[24,36,"pl-c1"],[36,37,"pl-s"],[38,39,"pl-ml"],[66,67,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[23,24,"pl-s"],[24,45,"pl-c1"],[45,46,"pl-s"],[47,48,"pl-ml"],[96,97,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[43,44,"pl-s"],[44,58,"pl-c1"],[58,59,"pl-s"],[92,93,"pl-s"],[93,110,"pl-c1"],[110,111,"pl-s"],[112,113,"pl-ml"],[209,210,"pl-s"],[210,217,"pl-c1"],[217,218,"pl-s"],[219,220,"pl-ml"]],[],[],[],[[0,1,"pl-v"],[58,59,"pl-s"],[59,73,"pl-c1"],[73,74,"pl-s"]],[[0,1,"pl-v"],[91,92,"pl-s"],[92,109,"pl-c1"],[109,110,"pl-s"]],[[0,1,"pl-v"],[50,51,"pl-s"],[51,60,"pl-c1"],[60,61,"pl-s"],[131,132,"pl-s"],[132,141,"pl-c1"],[141,142,"pl-s"],[144,145,"pl-s"],[145,154,"pl-c1"],[154,155,"pl-s"]],[],[[0,30,"pl-mh"],[4,30,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"],[43,44,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,31,"pl-c1"],[31,32,"pl-s"],[33,34,"pl-ml"],[44,45,"pl-ml"],[46,47,"pl-s"],[47,61,"pl-c1"],[61,62,"pl-s"],[64,65,"pl-s"],[65,79,"pl-c1"],[79,80,"pl-s"],[81,82,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,25,"pl-c1"],[25,26,"pl-s"],[27,28,"pl-ml"],[29,30,"pl-s"],[30,56,"pl-c1"],[56,57,"pl-s"],[59,60,"pl-s"],[60,86,"pl-c1"],[86,87,"pl-s"],[88,89,"pl-ml"],[122,123,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"],[29,30,"pl-ml"],[31,32,"pl-s"],[32,58,"pl-c1"],[58,59,"pl-s"],[61,62,"pl-s"],[62,88,"pl-c1"],[88,89,"pl-s"],[90,91,"pl-ml"],[124,125,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"],[28,29,"pl-ml"],[30,31,"pl-s"],[31,47,"pl-c1"],[47,48,"pl-s"],[50,51,"pl-s"],[51,67,"pl-c1"],[67,68,"pl-s"],[86,87,"pl-s"],[87,95,"pl-c1"],[95,96,"pl-s"],[97,98,"pl-ml"],[119,120,"pl-s"],[120,124,"pl-c1"],[124,125,"pl-s"],[134,135,"pl-s"],[135,140,"pl-c1"],[140,141,"pl-s"],[142,143,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,26,"pl-c1"],[26,27,"pl-s"],[28,29,"pl-ml"],[30,31,"pl-s"],[31,48,"pl-c1"],[48,49,"pl-s"],[51,52,"pl-s"],[52,69,"pl-c1"],[69,70,"pl-s"],[71,72,"pl-ml"],[95,96,"pl-ml"]],[],[],[],[[0,1,"pl-v"],[24,25,"pl-s"],[25,32,"pl-c1"],[32,33,"pl-s"]],[[0,1,"pl-v"]],[[0,1,"pl-v"],[52,53,"pl-s"],[53,56,"pl-c1"],[56,57,"pl-s"],[60,61,"pl-s"],[61,64,"pl-c1"],[64,65,"pl-s"],[104,105,"pl-s"],[105,110,"pl-c1"],[110,111,"pl-s"]],[],[[0,30,"pl-mh"],[4,30,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"],[43,44,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,34,"pl-c1"],[34,35,"pl-s"],[36,37,"pl-ml"],[47,48,"pl-ml"],[49,50,"pl-s"],[50,67,"pl-c1"],[67,68,"pl-s"],[70,71,"pl-s"],[71,88,"pl-c1"],[88,89,"pl-s"],[90,91,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,28,"pl-c1"],[28,29,"pl-s"],[30,31,"pl-ml"],[32,33,"pl-s"],[33,59,"pl-c1"],[59,60,"pl-s"],[62,63,"pl-s"],[63,89,"pl-c1"],[89,90,"pl-s"],[91,92,"pl-ml"],[93,94,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"],[32,33,"pl-ml"],[34,35,"pl-s"],[35,61,"pl-c1"],[61,62,"pl-s"],[64,65,"pl-s"],[65,91,"pl-c1"],[91,92,"pl-s"],[93,94,"pl-ml"],[95,96,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,29,"pl-c1"],[29,30,"pl-s"],[31,32,"pl-ml"],[33,34,"pl-s"],[34,50,"pl-c1"],[50,51,"pl-s"],[53,54,"pl-s"],[54,70,"pl-c1"],[70,71,"pl-s"],[89,90,"pl-s"],[90,98,"pl-c1"],[98,99,"pl-s"],[100,101,"pl-ml"],[102,103,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,29,"pl-c1"],[29,30,"pl-s"],[31,32,"pl-ml"],[33,34,"pl-s"],[34,51,"pl-c1"],[51,52,"pl-s"],[54,55,"pl-s"],[55,72,"pl-c1"],[72,73,"pl-s"],[74,75,"pl-ml"],[76,77,"pl-ml"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"],[53,54,"pl-s"],[54,63,"pl-c1"],[63,64,"pl-s"],[69,70,"pl-s"],[70,79,"pl-c1"],[79,80,"pl-s"]],[],[[0,18,"pl-mh"],[4,18,"pl-en"]],[],[[14,15,"pl-s"],[15,26,"pl-c1"],[26,27,"pl-s"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[],[],[],[],[],[[0,1,"pl-v"],[33,34,"pl-s"],[34,44,"pl-c1"],[44,45,"pl-s"]],[[0,1,"pl-v"],[18,19,"pl-s"],[19,29,"pl-c1"],[29,30,"pl-s"],[34,35,"pl-s"],[35,52,"pl-c1"],[52,53,"pl-s"]],[[0,1,"pl-v"],[35,36,"pl-s"],[36,49,"pl-c1"],[49,50,"pl-s"]],[],[[0,23,"pl-mh"],[5,23,"pl-en"]],[],[[4,5,"pl-s"],[5,22,"pl-c1"],[22,23,"pl-s"]],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[26,27,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"],[9,10,"pl-ml"],[11,12,"pl-s"],[12,43,"pl-c1"],[43,44,"pl-s"],[45,46,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[13,14,"pl-s"],[14,45,"pl-c1"],[45,46,"pl-s"],[47,48,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[12,13,"pl-s"],[13,34,"pl-c1"],[34,35,"pl-s"],[36,37,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[12,13,"pl-s"],[13,35,"pl-c1"],[35,36,"pl-s"],[37,38,"pl-ml"]],[],[[88,89,"pl-s"],[89,106,"pl-c1"],[106,107,"pl-s"],[121,122,"pl-s"],[122,127,"pl-c1"],[127,128,"pl-s"]],[],[[0,30,"pl-mh"],[5,30,"pl-en"]],[],[[4,5,"pl-s"],[5,18,"pl-c1"],[18,19,"pl-s"],[31,32,"pl-s"],[32,38,"pl-c1"],[38,39,"pl-s"],[47,48,"pl-s"],[48,53,"pl-c1"],[53,54,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,5,"pl-c1"],[5,6,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,15,"pl-c1"],[15,16,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"],[36,37,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[26,27,"pl-ml"],[28,29,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"],[9,10,"pl-ml"],[11,12,"pl-s"],[12,38,"pl-c1"],[38,39,"pl-s"],[40,41,"pl-ml"],[63,64,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[13,14,"pl-s"],[14,37,"pl-c1"],[37,38,"pl-s"],[39,40,"pl-ml"],[62,63,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[12,13,"pl-s"],[13,27,"pl-c1"],[27,28,"pl-s"],[29,30,"pl-ml"],[52,53,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[42,43,"pl-s"],[43,46,"pl-c1"],[46,47,"pl-s"],[69,70,"pl-ml"],[98,99,"pl-s"],[99,116,"pl-c1"],[116,117,"pl-s"],[140,141,"pl-ml"]],[],[[8,9,"pl-s"],[9,23,"pl-c1"],[23,24,"pl-s"]],[],[[0,1,"pl-v"],[84,85,"pl-s"],[85,90,"pl-c1"],[90,91,"pl-s"]],[[0,1,"pl-v"],[6,7,"pl-s"],[7,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[57,58,"pl-s"],[58,63,"pl-c1"],[63,64,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,10,"pl-mh"],[3,10,"pl-en"],[3,4,"pl-s"],[4,9,"pl-c1"],[9,10,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,33,"pl-c1"],[33,34,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,39,"pl-c1"],[39,40,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"],[36,37,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,19,"pl-c1"],[19,20,"pl-s"],[21,22,"pl-ml"],[30,31,"pl-s"],[31,45,"pl-c1"],[45,46,"pl-s"],[47,48,"pl-ml"],[62,63,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"],[22,23,"pl-ml"],[69,70,"pl-s"],[70,85,"pl-c1"],[85,86,"pl-s"],[105,106,"pl-s"],[106,112,"pl-c1"],[112,113,"pl-s"],[114,115,"pl-ml"],[116,117,"pl-s"],[117,123,"pl-c1"],[123,124,"pl-s"],[173,174,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"],[18,19,"pl-ml"],[36,37,"pl-s"],[37,52,"pl-c1"],[52,53,"pl-s"],[72,73,"pl-s"],[73,79,"pl-c1"],[79,80,"pl-s"],[81,82,"pl-ml"],[83,84,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,23,"pl-c1"],[23,24,"pl-s"],[25,26,"pl-ml"],[43,44,"pl-s"],[44,59,"pl-c1"],[59,60,"pl-s"],[79,80,"pl-s"],[80,86,"pl-c1"],[86,87,"pl-s"],[88,89,"pl-ml"],[90,91,"pl-ml"]],[],[],[],[[0,1,"pl-v"],[30,31,"pl-s"],[31,46,"pl-c1"],[46,47,"pl-s"],[81,82,"pl-s"],[82,87,"pl-c1"],[87,88,"pl-s"],[108,109,"pl-s"],[109,123,"pl-c1"],[123,124,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,11,"pl-mh"],[3,11,"pl-en"],[3,4,"pl-s"],[4,10,"pl-c1"],[10,11,"pl-s"]],[],[],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,9,"pl-c1"],[9,10,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"]],[],[[0,20,"pl-mh"],[4,20,"pl-en"],[4,5,"pl-s"],[5,19,"pl-c1"],[19,20,"pl-s"]],[],[],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[26,27,"pl-ml"],[40,41,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"],[12,13,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[39,40,"pl-s"],[40,49,"pl-c1"],[49,50,"pl-s"],[52,53,"pl-s"],[53,67,"pl-c1"],[67,68,"pl-s"],[70,71,"pl-s"],[71,75,"pl-c1"],[75,76,"pl-s"],[84,85,"pl-ml"],[150,151,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,15,"pl-c1"],[15,16,"pl-s"],[17,18,"pl-ml"],[57,58,"pl-ml"],[68,69,"pl-s"],[69,84,"pl-c1"],[84,85,"pl-s"],[87,88,"pl-s"],[88,103,"pl-c1"],[103,104,"pl-s"],[106,107,"pl-s"],[107,117,"pl-c1"],[117,118,"pl-s"],[119,120,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,8,"pl-c1"],[8,9,"pl-s"],[10,11,"pl-ml"],[41,42,"pl-ml"],[50,51,"pl-s"],[51,52,"pl-c1"],[52,53,"pl-s"],[54,55,"pl-s"],[55,56,"pl-c1"],[56,57,"pl-s"],[58,59,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,14,"pl-c1"],[14,15,"pl-s"],[16,17,"pl-ml"],[49,50,"pl-ml"],[92,93,"pl-ml"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,41,"pl-c1"],[41,42,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,33,"pl-c1"],[33,34,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,33,"pl-c1"],[33,34,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[],[[127,128,"pl-s"],[128,135,"pl-c1"],[135,136,"pl-s"],[142,143,"pl-s"],[143,154,"pl-c1"],[154,155,"pl-s"],[160,161,"pl-s"],[161,176,"pl-c1"],[176,177,"pl-s"]],[],[[4,5,"pl-s"],[5,18,"pl-c1"],[18,19,"pl-s"],[106,107,"pl-s"],[107,110,"pl-c1"],[110,111,"pl-s"]],[],[[0,12,"pl-mh"],[4,12,"pl-en"],[4,5,"pl-s"],[5,11,"pl-c1"],[11,12,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"]],[],[],[],[[0,3,"pl-ms"]],[],[[0,26,"pl-mh"],[3,26,"pl-en"],[16,17,"pl-s"],[17,25,"pl-c1"],[25,26,"pl-s"]],[],[[0,17,"pl-mh"],[3,17,"pl-en"],[3,4,"pl-s"],[4,16,"pl-c1"],[16,17,"pl-s"]],[],[[0,21,"pl-mh"],[4,21,"pl-en"],[4,5,"pl-s"],[5,20,"pl-c1"],[20,21,"pl-s"]],[],[],[],[],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[[0,1,"pl-v"],[7,8,"pl-s"],[8,19,"pl-c1"],[19,20,"pl-s"],[46,47,"pl-s"],[47,55,"pl-c1"],[55,56,"pl-s"]],[[0,1,"pl-v"],[7,8,"pl-s"],[8,20,"pl-c1"],[20,21,"pl-s"],[45,46,"pl-s"],[46,50,"pl-c1"],[50,51,"pl-s"]],[],[[0,21,"pl-mh"],[4,21,"pl-en"],[4,5,"pl-s"],[5,20,"pl-c1"],[20,21,"pl-s"]],[],[],[],[],[],[[0,1,"pl-v"],[72,73,"pl-s"],[73,78,"pl-c1"],[78,79,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,29,"pl-mh"],[3,29,"pl-en"],[3,4,"pl-s"],[4,18,"pl-c1"],[18,19,"pl-s"],[22,23,"pl-s"],[23,28,"pl-c1"],[28,29,"pl-s"]],[],[],[],[[0,26,"pl-mh"],[4,26,"pl-en"]],[],[[0,1,"pl-ml"],[11,12,"pl-ml"],[22,23,"pl-ml"]],[[0,1,"pl-ml"],[4,5,"pl-ml"],[8,9,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[15,16,"pl-ml"],[41,42,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[37,38,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"],[13,14,"pl-ml"],[39,40,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,9,"pl-c1"],[9,10,"pl-s"],[11,12,"pl-ml"],[34,35,"pl-ml"]],[[0,1,"pl-ml"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"],[14,15,"pl-ml"],[37,38,"pl-ml"]],[],[[0,32,"pl-mh"],[4,32,"pl-en"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,16,"pl-c1"],[16,17,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,34,"pl-c1"],[34,35,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[10,11,"pl-s"],[11,14,"pl-c1"],[14,15,"pl-s"],[34,35,"pl-s"],[35,40,"pl-c1"],[40,41,"pl-s"],[60,61,"pl-s"],[61,74,"pl-c1"],[74,75,"pl-s"]],[],[[121,122,"pl-s"],[122,127,"pl-c1"],[127,128,"pl-s"],[167,168,"pl-s"],[168,175,"pl-c1"],[175,176,"pl-s"]],[],[[0,21,"pl-mh"],[4,21,"pl-en"],[4,5,"pl-s"],[5,20,"pl-c1"],[20,21,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,7,"pl-c1"],[7,8,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,12,"pl-c1"],[12,13,"pl-s"]],[],[],[],[[0,1,"pl-v"]],[],[[0,19,"pl-mh"],[4,19,"pl-en"],[4,5,"pl-s"],[5,18,"pl-c1"],[18,19,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,13,"pl-c1"],[13,14,"pl-s"],[35,36,"pl-s"],[36,43,"pl-c1"],[43,44,"pl-s"]],[[0,1,"pl-v"],[69,70,"pl-s"],[70,71,"pl-c1"],[71,72,"pl-s"]],[],[[0,22,"pl-mh"],[4,22,"pl-en"],[4,5,"pl-s"],[5,21,"pl-c1"],[21,22,"pl-s"]],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,20,"pl-c1"],[20,21,"pl-s"]],[[0,1,"pl-v"]],[[0,1,"pl-v"]],[],[[0,14,"pl-mh"],[4,14,"pl-en"],[4,5,"pl-s"],[5,13,"pl-c1"],[13,14,"pl-s"]],[],[],[],[],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,10,"pl-c1"],[10,11,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,49,"pl-mh"],[3,49,"pl-en"]],[],[],[],[[0,24,"pl-mh"],[4,24,"pl-en"]],[],[[31,32,"pl-s"],[32,39,"pl-c1"],[39,40,"pl-s"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,23,"pl-c1"],[23,24,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,40,"pl-c1"],[40,41,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,28,"pl-c1"],[28,29,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,34,"pl-c1"],[34,35,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,42,"pl-c1"],[42,43,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,37,"pl-c1"],[37,38,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,46,"pl-c1"],[46,47,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,27,"pl-c1"],[27,28,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,24,"pl-c1"],[24,25,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,29,"pl-c1"],[29,30,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,30,"pl-c1"],[30,31,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,31,"pl-c1"],[31,32,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,32,"pl-c1"],[32,33,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,35,"pl-c1"],[35,36,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,44,"pl-c1"],[44,45,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,61,"pl-c1"],[61,62,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,64,"pl-c1"],[64,65,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,62,"pl-c1"],[62,63,"pl-s"]],[],[[0,35,"pl-mh"],[4,35,"pl-en"]],[],[[0,1,"pl-v"],[2,3,"pl-s"],[3,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,11,"pl-c1"],[11,12,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,15,"pl-c1"],[15,16,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,31,"pl-c1"],[31,32,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,31,"pl-c1"],[31,32,"pl-s"]],[[0,1,"pl-v"],[2,3,"pl-s"],[3,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-v"],[12,13,"pl-s"],[13,26,"pl-c1"],[26,27,"pl-s"],[44,45,"pl-s"],[45,60,"pl-c1"],[60,61,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,36,"pl-mh"],[3,36,"pl-en"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,14,"pl-c1"],[14,15,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,8,"pl-c1"],[8,9,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,9,"pl-c1"],[9,10,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,17,"pl-c1"],[17,18,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,21,"pl-c1"],[21,22,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,24,"pl-c1"],[24,25,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,18,"pl-c1"],[18,19,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,21,"pl-c1"],[21,22,"pl-s"]],[],[[20,21,"pl-s"],[21,28,"pl-c1"],[28,29,"pl-s"]],[],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"]],[[0,1,"pl-s"],[1,2,"pl-v"],[59,60,"pl-s"],[60,65,"pl-c1"],[65,66,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"]],[],[],[],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,9,"pl-c1"],[9,10,"pl-s"],[13,14,"pl-s"],[14,28,"pl-c1"],[28,29,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,22,"pl-c1"],[22,23,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,25,"pl-c1"],[25,26,"pl-s"]],[[0,1,"pl-s"],[1,2,"pl-v"],[3,4,"pl-s"],[4,17,"pl-c1"],[17,18,"pl-s"]],[],[[0,3,"pl-ms"]],[],[[0,30,"pl-mh"],[3,30,"pl-en"]],[],[],[],[[0,1,"pl-v"],[47,48,"pl-s"],[48,58,"pl-c1"],[58,59,"pl-s"]],[[0,1,"pl-v"],[33,34,"pl-s"],[34,39,"pl-c1"],[39,40,"pl-s"]],[[0,1,"pl-v"],[36,37,"pl-s"],[37,46,"pl-c1"],[46,47,"pl-s"]],[[0,1,"pl-v"],[17,18,"pl-s"],[18,31,"pl-c1"],[31,32,"pl-s"],[42,43,"pl-s"],[43,46,"pl-c1"],[46,47,"pl-s"]],[[0,1,"pl-v"]],[],[[361,362,"pl-s"],[362,369,"pl-c1"],[369,370,"pl-s"]]],"colorizedLines":null}},"title":"scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile","appPayload":{},"meta":{"title":"scada-mobile/FRONTEND_DATA_SOURCES.md at main · savushkin-dev/scada-mobile"}}</script>
  <div data-target="react-app.reactRoot"><meta name="github-code-view-meta-stats" id="github-code-view-meta-stats" data-hydrostats="publish"/> <!-- --> <a hidden="" id="code-view-repo-link" href="/savushkin-dev/scada-mobile" data-discover="true"></a> <div class="d-none"></div><div><div style="--spacing:var(--spacing-none)" class="prc-PageLayout-PageLayoutRoot--KH-d" data-component="SplitPageLayout" data-has-sidebar="true"><div class="prc-PageLayout-SidebarWrapper-kLG4B CopilotSidePanelSidebar-module__SidePanel__L3O0C CopilotSidePanelSidebar-module__HiddenSidePanel__TBRGn" style="--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="end" data-sticky="true" data-responsive-variant="fullscreen"><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-SidebarVerticalDivider-0Rl0V" data-component="PageLayout.VerticalDivider" data-variant="line" data-position="end" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="450" aria-valuemax="768" aria-valuenow="544" aria-valuetext="Pane width 544 pixels" tabindex="0"></div></div><div class="prc-PageLayout-Sidebar-iciWg" data-component="SplitPageLayout.Sidebar" data-resizable="true" style="--spacing:var(--spacing-normal);--pane-min-width:450px;--pane-max-width:768px;--pane-width-custom:544px;--pane-width-size:var(--pane-width-custom);--pane-width:544px"><div class="height-full" data-testid="copilot-code-view-side-panel"><div id="copilot-side-panel-content" class="height-full"></div></div></div></div><div class="prc-PageLayout-PageLayoutWrapper-2BhU2" data-width="full"><div class="prc-PageLayout-PageLayoutContent-BneH9"><div id="repos-file-tree-sidebar" class="CodeViewFileTreeLayout-module__sidebar__n_Aau" tabindex="0"><div class="prc-PageLayout-PaneWrapper-pHPop ReposFileTreePane-module__Pane__rBZpI ReposFileTreePane-module__HideTree__AYZnm ReposFileTreePane-module__HidePane__VHAVt" style="--offset-header:0px;--spacing-row:var(--spacing-none);--spacing-column:var(--spacing-none)" data-is-hidden="false" data-position="start" data-sticky="true"><div class="prc-PageLayout-HorizontalDivider-JLVqp prc-PageLayout-PaneHorizontalDivider-9tbnE" data-component="PageLayout.HorizontalDivider" data-variant-regular="none" data-variant-narrow="none" data-position="start" style="--spacing-divider:var(--spacing-none);--spacing:var(--spacing-none)"></div><div class="prc-PageLayout-Pane-AyzHK" data-component="SplitPageLayout.Pane" data-resizable="true" style="--spacing:var(--spacing-none);--pane-min-width:256px;--pane-max-width:calc(100vw - var(--pane-max-width-diff));--pane-width-size:var(--pane-width-large);--pane-width:320px"></div><div class="prc-PageLayout-VerticalDivider-9QRmK prc-PageLayout-PaneVerticalDivider-le57g" data-component="PageLayout.VerticalDivider" data-variant-narrow="none" data-variant-regular="line" data-variant-wide="line" data-position="start" style="--spacing:var(--spacing-none)"><div class="prc-PageLayout-DraggableHandle-9s6B4" data-component="PageLayout.DragHandle" role="slider" aria-label="Draggable pane splitter" aria-valuemin="256" aria-valuemax="600" aria-valuenow="320" aria-valuetext="Pane width 320 pixels" tabindex="0"></div></div></div></div><div data-component="SplitPageLayout.Content" class="prc-PageLayout-ContentWrapper-gR9eG"><div class="prc-PageLayout-Content-xWL-A" data-width="full" style="--spacing:var(--spacing-none)"><div class="SharedPageLayout-module__content__IwGAp" data-selector="repos-split-pane-content" id="repos-split-pane-content" tabindex="0"> <!-- --> <div class="container CodeViewHeader-module__Box__JkPOb"><div class="CodeViewHeader-module__StickyHeader__Qn7UN" id="StickyHeader"><div class="CodeViewHeader-module__Box_1__SbNDV"><div class="CodeViewHeader-module__Box_2__TB46f"><div class="react-code-view-header-wrap--narrow CodeViewHeader-module__Box_3__q1zUL"><div class="CodeViewHeader-module__treeToggleWrapper__RQ__9"><h2 class="use-tree-pane-module__Heading__s4QbZ prc-Heading-Heading-MtWFE" data-component="Heading"><button data-component="Button" type="button" aria-label="Expand file tree" data-testid="expand-file-tree-button-mobile" class="prc-Button-ButtonBase-9n-Xk ExpandFileTreeButton-module__Button_1__Svs95" data-loading="false" data-size="medium" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-left" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M7.78 12.53a.75.75 0 0 1-1.06 0L2.47 8.28a.75.75 0 0 1 0-1.06l4.25-4.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042L4.81 7h7.44a.75.75 0 0 1 0 1.5H4.81l2.97 2.97a.75.75 0 0 1 0 1.06Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Files</span></span></button><button data-component="IconButton" type="button" data-testid="expand-file-tree-button" aria-controls="repos-file-tree" class="prc-Button-ButtonBase-9n-Xk position-relative ExpandFileTreeButton-module__expandButton__hDOcv ExpandFileTreeButton-module__filesButtonBreakpoint__zEvz3 fgColor-muted prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="invisible" aria-labelledby="_R_4lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-sidebar-collapse" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M6.823 7.823a.25.25 0 0 1 0 .354l-2.396 2.396A.25.25 0 0 1 4 10.396V5.604a.25.25 0 0 1 .427-.177Z"></path><path d="M1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0 1 14.25 16H1.75A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0ZM1.5 1.75v12.5c0 .138.112.25.25.25H9.5v-13H1.75a.25.25 0 0 0-.25.25ZM11 14.5h3.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25H11Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="se" data-component="Tooltip" aria-hidden="true" id="_R_4lla9lik5_">Expand file tree</span><div class="d-none"></div></h2></div><div class="react-code-view-header-mb--narrow mr-2"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="main branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector-wide"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">main</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="react-code-view-header-mb--narrow CodeViewHeader-module__Box_5__MQ0hL"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__lg__Rjz0A"><nav data-testid="breadcrumbs" aria-labelledby="repos-header-breadcrumb-heading" id="repos-header-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="repos-header-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/savushkin-dev/scada-mobile/tree/main" data-discover="true">scada-mobile</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__lg__Rjz0A" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__lg__Rjz0A prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="file-name-id">FRONTEND_DATA_SOURCES.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lla9lik5_">Copy path</span></div></div></div><div class="react-code-view-header-element--wide"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><div><div class="CodeViewHeader-module__FileResultsList__JDzUy"><span class="d-flex FileResultsList-module__FilesSearchBox__ivVkc TextInput-wrapper prc-components-TextInputWrapper-Hpdqi prc-components-TextInputBaseWrapper-wY-n0" data-no-trailing-action="true" data-component="TextInput" data-leading-visual="true" data-trailing-visual="true" aria-busy="false"><span class="TextInput-icon" id="_R_1cpla9lik5_" aria-hidden="true" data-component="TextInput.LeadingVisual"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-search" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M10.68 11.74a6 6 0 0 1-7.922-8.982 6 6 0 0 1 8.982 7.922l3.04 3.04a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215ZM11.5 7a4.499 4.499 0 1 0-8.997 0A4.499 4.499 0 0 0 11.5 7Z"></path></svg></span><input type="text" aria-label="Go to file" role="combobox" aria-controls="file-results-list" aria-expanded="false" aria-haspopup="dialog" autoCorrect="off" spellCheck="false" placeholder="Go to file" aria-describedby="_R_1cpla9lik5_ _R_1cpla9lik5H1_" data-component="input" class="prc-components-Input-IwWrt" value=""/><span class="TextInput-icon" id="_R_1cpla9lik5H1_" aria-hidden="true" data-component="TextInput.TrailingVisual"></span></span></div><div class="d-none"></div></div><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-wide" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7p9la9lik5_" id="_R_99la9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7p9la9lik5_">More file actions</span></div></div></div><div class="react-code-view-header-element--narrow"><div class="CodeViewHeader-module__Box_7___0R6c"><div class="d-flex gap-2"><button data-component="Button" type="button" style="display:none" class="prc-Button-ButtonBase-9n-Xk NavigationMenu-module__Button__LpKgm" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Blame</span></span></button><div class="d-none"></div><button data-component="IconButton" type="button" data-testid="more-file-actions-button-nav-menu-narrow" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click NavigationMenu-module__IconButton__HpX3G prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="medium" data-variant="default" aria-labelledby="_R_7pdla9lik5_" id="_R_9dla9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_7pdla9lik5_">More file actions</span></div></div></div></div></div></div></div><div class="CodeView-module__contentWrapper__cG2JH"><div class="react-code-view-bottom-padding"><div class="BlobTopBanners-module__Box__v_nvx"></div></div> <div class="d-none"></div><div class="d-flex flex-column border rounded-2 tmp-mb-3 pl-1"><div class="LatestCommit-module__Box__B25ZT"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">Latest commit</h2><div style="width:120px" class="Skeleton Skeleton--text" data-testid="loading"> </div><div class="d-flex flex-shrink-0 gap-2"><div data-testid="latest-commit-details" class="d-none d-sm-flex flex-items-center"></div><div class="d-flex gap-2"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">History</h2><a data-component="LinkButton" href="/savushkin-dev/scada-mobile/commits/main/FRONTEND_DATA_SOURCES.md" class="prc-Button-ButtonBase-9n-Xk d-none d-lg-flex LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><span class="fgColor-default">History</span></span></span></a><div class="d-sm-none"></div><div class="d-flex d-lg-none"><a data-component="LinkButton" aria-label="View commit history for this file." href="/savushkin-dev/scada-mobile/commits/main/FRONTEND_DATA_SOURCES.md" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov flex-items-center fgColor-default" data-loading="false" data-size="small" data-variant="invisible" aria-describedby="_R_4mlala9lik5_"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-history" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m.427 1.927 1.215 1.215a8.002 8.002 0 1 1-1.6 5.685.75.75 0 1 1 1.493-.154 6.5 6.5 0 1 0 1.18-4.458l1.358 1.358A.25.25 0 0 1 3.896 6H.25A.25.25 0 0 1 0 5.75V2.104a.25.25 0 0 1 .427-.177ZM7.75 4a.75.75 0 0 1 .75.75v2.992l2.028.812a.75.75 0 0 1-.557 1.392l-2.5-1A.751.751 0 0 1 7 8.25v-3.5A.75.75 0 0 1 7.75 4Z"></path></svg></span></span></a><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" role="tooltip" aria-hidden="true" id="_R_4mlala9lik5_">History</span></div></div></div></div></div><div class="d-flex flex-row"><div class="container BlobViewContent-module__blobContainer__DtH2d"><div class="react-code-size-details-banner BlobViewContent-module__codeSizeDetails__e5sUw"><div class="react-code-size-details-banner CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="33.1 KB" style="--truncate-max-width:100%"><span>655 lines (452 loc) · 33.1 KB</span></div></div></div></div><div class="react-blob-view-header-sticky BlobViewContent-module__stickyHeader__VwxB5" id="repos-sticky-header"><div class="BlobViewHeader-module__Box__yhm9u"><div class="react-blob-sticky-header"><div class="FileNameStickyHeader-module__outerWrapper__ZL4Xc FileNameStickyHeader-module__outerWrapperHidden__Zpynk"><div class="FileNameStickyHeader-module__Box_1__Hazu5"><div class="FileNameStickyHeader-module__Box_2__hoolP"><div class="FileNameStickyHeader-module__Box_3__MVKsk"><button data-component="Button" type="button" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-label="main branch" data-testid="anchor-button" data-icv-name="Switch branches/tags" class="prc-Button-ButtonBase-9n-Xk ref-selector-class RefSelectorAnchoredOverlay-module__RefSelectorOverlayBtn__a3WK3" data-loading="false" data-size="medium" data-variant="default" id="ref-picker-repos-header-ref-selector"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-git-branch" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M9.5 3.25a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25Zm-6 0a.75.75 0 1 0 1.5 0 .75.75 0 0 0-1.5 0Zm8.25-.75a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5ZM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3"><div class="RefSelectorAnchoredOverlay-module__RefSelectorOverlayContainer__yaf4p"><div style="max-width:125px" class="ref-selector-button-text-container RefSelectorAnchoredOverlay-module__RefSelectorBtnTextContainer__Di3rk"><span class="RefSelectorAnchoredOverlay-module__RefSelectorText__w_fmP">main</span></div></div></span><span data-component="trailingVisual" class="prc-Button-Visual-YNt2F prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-triangle-down" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"></path></svg></span></span></button><div class="d-none"></div></div><div class="FileNameStickyHeader-module__Box_4__FLhtt"><div class="Breadcrumb-module__container__Vxvev Breadcrumb-module__md__Wb1Gs"><nav data-testid="breadcrumbs" aria-labelledby="sticky-breadcrumb-heading" id="sticky-breadcrumb" class="Breadcrumb-module__nav__rQFDj"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading" id="sticky-breadcrumb-heading">Breadcrumbs</h2><ol class="Breadcrumb-module__list__ZH6zr"><li class="Breadcrumb-module__listItem__Ib0x_"><a class="Breadcrumb-module__repoLink__O2Nbs prc-Link-Link-9ZwDx" data-component="Link" data-testid="breadcrumbs-repo-link" href="/savushkin-dev/scada-mobile/tree/main" data-discover="true">scada-mobile</a></li></ol></nav><div data-testid="breadcrumbs-filename" class="Breadcrumb-module__filename__equZR"><span class="Breadcrumb-module__separator__eNwsI Breadcrumb-module__md__Wb1Gs" aria-hidden="true">/</span><h1 class="Breadcrumb-module__filenameHeading__MNMtw Breadcrumb-module__md__Wb1Gs prc-Heading-Heading-MtWFE" data-component="Heading" tabindex="-1" id="sticky-file-name-id">FRONTEND_DATA_SOURCES.md</h1></div><button data-component="IconButton" type="button" class="prc-Button-ButtonBase-9n-Xk ml-2 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_7lcpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="CopyToClipboardIconButton-module__tooltip__WyiwL prc-TooltipV2-Tooltip-tLeuB" data-direction="s" data-component="Tooltip" aria-label="Copy path" aria-hidden="true" id="_R_7lcpala9lik5_">Copy path</span></div></div></div><button data-component="Button" type="button" class="prc-Button-ButtonBase-9n-Xk FileNameStickyHeader-module__Button__LSEU_ FileNameStickyHeader-module__GoToTopButton__nxAFn" data-loading="false" data-size="small" data-variant="invisible"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="leadingVisual" class="prc-Button-Visual-YNt2F prc-Button-LeadingVisual-UySKu prc-Button-VisualWrap-E4cnq"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-arrow-up" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M3.47 7.78a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0l4.25 4.25a.751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018L9 4.81v7.44a.75.75 0 0 1-1.5 0V4.81L4.53 7.78a.75.75 0 0 1-1.06 0Z"></path></svg></span><span data-component="text" class="prc-Button-Label-FWkx3">Top</span></span></button></div></div></div><div class="BlobViewHeader-module__Box_1__VEmuQ"><h2 class="sr-only ScreenReaderHeading-module__userSelectNone__rwWIk prc-Heading-Heading-MtWFE" data-component="Heading" data-testid="screen-reader-heading">File metadata and controls</h2><div class="BlobViewHeader-module__Box_2__icUs2"><ul aria-label="File view" class="prc-SegmentedControl-SegmentedControl-lqIXp BlobTabButtons-module__SegmentedControl__jen2u" data-variant="default" data-size="small" data-component="SegmentedControl"><li class="prc-SegmentedControl-Item-tSCQh" data-selected="" data-component="SegmentedControl.Button"><button aria-pressed="true" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:transparent"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Preview">Preview</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Code">Code</div></span></button></li><li class="prc-SegmentedControl-Item-tSCQh" data-component="SegmentedControl.Button"><button aria-pressed="false" class="prc-SegmentedControl-Button-E48xz" type="button" style="--separator-color:var(--borderColor-default)"><span class="prc-SegmentedControl-Content-1COlk segmentedControl-content"><div class="prc-SegmentedControl-Text-7S2y2 segmentedControl-text" data-text="Blame">Blame</div></span></button></li></ul><div class="d-none"></div><div class="react-code-size-details-in-header CodeSizeDetails-module__Box__VcD6l"><div class="text-mono CodeSizeDetails-module__Box_1__GVxQL"><div data-testid="blob-size" class="CodeSizeDetails-module__Truncate_1__lE93V prc-Truncate-Truncate-2G1eo" data-inline="true" title="33.1 KB" style="--truncate-max-width:100%"><span>655 lines (452 loc) · 33.1 KB</span></div></div></div></div><div class="BlobViewHeader-module__Box_3__ng6v2"><div class="d-none"></div><div class="react-blob-header-edit-and-raw-actions BlobViewHeader-module__Box_4__J4Y4W"><div class="d-none"></div><div class="prc-ButtonGroup-ButtonGroup-vFUrY" data-component="ButtonGroup"><div class="prc-ButtonGroup-Item-PqvDl"><a data-component="LinkButton" href="https://github.com/savushkin-dev/scada-mobile/raw/refs/heads/main/FRONTEND_DATA_SOURCES.md" data-testid="raw-button" class="prc-Button-ButtonBase-9n-Xk LinkButton-module__linkButton__nFnov BlobViewHeader-module__LinkButton__X9kx2" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default"><span data-component="buttonContent" data-align="center" class="prc-Button-ButtonContent-Iohp5"><span data-component="text" class="prc-Button-Label-FWkx3">Raw</span></span></a></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="copy-raw-button" class="prc-Button-ButtonBase-9n-Xk prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_qaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-copy" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_qaucpala9lik5_">Copy raw file</span></div><div class="prc-ButtonGroup-Item-PqvDl"><button data-component="IconButton" type="button" data-testid="download-raw-button" class="prc-Button-ButtonBase-9n-Xk BlobViewHeader-module__downloadButton__ef459 prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="default" aria-labelledby="_R_eaucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-download" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14Z"></path><path d="M7.25 7.689V2a.75.75 0 0 1 1.5 0v5.689l1.97-1.969a.749.749 0 1 1 1.06 1.06l-3.25 3.25a.749.749 0 0 1-1.06 0L4.22 6.78a.749.749 0 1 1 1.06-1.06l1.97 1.969Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_eaucpala9lik5_">Download raw file</span></div></div></div><button data-component="IconButton" type="button" aria-pressed="false" class="prc-Button-ButtonBase-9n-Xk tmp-mr-2 TableOfContents-module__IconButton__jrlNM prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_3ucpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-list-unordered" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M5.75 2.5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5Zm0 5h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1 0-1.5ZM2 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-6a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM2 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="n" data-component="Tooltip" aria-hidden="true" id="_R_3ucpala9lik5_">Outline</span><div class="react-blob-header-edit-and-raw-actions-combined"><button data-component="IconButton" type="button" title="More file actions" data-testid="more-file-actions-button" aria-haspopup="true" aria-expanded="false" tabindex="0" class="prc-Button-ButtonBase-9n-Xk js-blob-dropdown-click BlobViewHeader-module__IconButton__XrMQY prc-Button-IconButton-fyge7" data-loading="false" data-no-visuals="true" data-size="small" data-variant="invisible" aria-labelledby="_R_fkecpala9lik5_" id="_R_kecpala9lik5_"><svg data-component="Octicon" aria-hidden="true" focusable="false" class="octicon octicon-kebab-horizontal" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" display="inline-block" overflow="visible" style="vertical-align:text-bottom"><path d="M8 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM1.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Zm13 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3Z"></path></svg></button><span class="prc-TooltipV2-Tooltip-tLeuB" data-direction="nw" data-component="Tooltip" aria-hidden="true" id="_R_fkecpala9lik5_">Edit and raw actions</span></div></div></div></div><div></div></div><div class="BlobViewContent-module__blobContentWrapper__JS0W6"><section aria-labelledby="file-name-id-wide file-name-id-mobile" class="BlobContent-module__blobContentSection__VOgZq BlobContent-module__blobContentSectionMarkdown__mPLOK" style="margin-top:46px"><div class="js-snippet-clipboard-copy-unpositioned BlobContent-module__markdownBlob__T8jpG" data-hpc="true" containertiming="hpc"><article class="markdown-body entry-content container-lg" itemprop="text"><div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">Источники данных для фронтенда</h1><a id="user-content-источники-данных-для-фронтенда" class="anchor" aria-label="Permalink: Источники данных для фронтенда" href="#источники-данных-для-фронтенда"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Purpose</h2><a id="user-content-purpose" class="anchor" aria-label="Permalink: Purpose" href="#purpose"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Фиксация подтвержденных источников данных для фронтенд-контракта и правил агрегации.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Table of contents</h2><a id="user-content-table-of-contents" class="anchor" aria-label="Permalink: Table of contents" href="#table-of-contents"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li><a href="#purpose">Purpose</a></li>
<li><a href="#%D1%87%D1%82%D0%BE-%D1%8D%D1%82%D0%BE-%D0%B7%D0%B0-%D0%B4%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82">Что это за документ</a></li>
<li><a href="#%D0%B2%D0%B0%D0%B6%D0%BD%D0%B0%D1%8F-%D0%BE%D0%B3%D0%BE%D0%B2%D0%BE%D1%80%D0%BA%D0%B0-%D0%BF%D0%BE-%D0%BE%D0%B1%D0%BB%D0%B0%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D0%BD%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8">Важная оговорка по области применимости</a></li>
<li><a href="#%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D1%8B%D0%B9-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4">Главный вывод</a></li>
<li><a href="#%D0%BF%D0%BE%D0%B4%D1%82%D0%B2%D0%B5%D1%80%D0%B6%D0%B4%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5-backend-%D0%B8%D1%81%D1%82%D0%BE%D1%87%D0%BD%D0%B8%D0%BA%D0%B8-%D0%B4%D0%BB%D1%8F-grunw11">Подтвержденные backend-источники для Grunw11</a></li>
<li><a href="#1-rest-topology">1. REST topology</a></li>
</ul>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Что это за документ</h2><a id="user-content-что-это-за-документ" class="anchor" aria-label="Permalink: Что это за документ" href="#что-это-за-документ"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Документ фиксирует, откуда именно backend может брать данные для контракта из <a href="/savushkin-dev/scada-mobile/blob/main/API_REFERENCE.md">API_REFERENCE.md</a>, если ориентироваться на подтвержденные источники PrintSrv и на карту соответствий из внешнего артефакта <code>GRUNW11_SCADA_OBJECT_MAPPING.md</code> (файл не хранится в этом репозитории).</p>
<p dir="auto">Текущая база анализа:</p>
<ul dir="auto">
<li>автомат <code>Grunw11</code></li>
<li>frontend-контракт из <a href="/savushkin-dev/scada-mobile/blob/main/API_REFERENCE.md">API_REFERENCE.md</a></li>
<li>карта SCADA/PrintSrv-источников из внешнего артефакта <code>GRUNW11_SCADA_OBJECT_MAPPING.md</code> (вне текущего репозитория)</li>
</ul>
<p dir="auto">Документ отвечает на вопрос: какие поля фронта можно брать напрямую, какие нужно агрегировать, а какие пока не подтверждены по данным Grunw11 и требуют отдельного решения в backend.</p>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Важная оговорка по области применимости</h2><a id="user-content-важная-оговорка-по-области-применимости" class="anchor" aria-label="Permalink: Важная оговорка по области применимости" href="#важная-оговорка-по-области-применимости"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Этот документ нельзя читать как универсальную схему для всех автоматов.</p>
<p dir="auto">Он построен на детальном анализе именно <code>Grunw11</code>, а <code>Grunw11</code> в составе устройств заметно богаче многих других автоматов. По реестру автоматов уже видно, что набор устройств в проекте отличается от линии к линии:</p>
<ul dir="auto">
<li>есть автоматы с одним принтером, например <code>Hassia1</code>, <code>Hassia2</code></li>
<li>есть автоматы с двумя принтерами, например <code>Bosch</code>, <code>Grunw1</code>, <code>Hassia3</code></li>
<li>есть автоматы с четырьмя принтерами и расширенным набором камер, как <code>Grunw11</code></li>
<li>есть автоматы с другим типом камер вообще, например <code>trepko1</code>, <code>trepko2</code></li>
</ul>
<p dir="auto">Из этого следует главное правило интеграции:</p>
<ul dir="auto">
<li>количество камер и принтеров зависит от автомата</li>
<li>список unit-объектов в PrintSrv зависит от автомата</li>
<li>набор агрегированных полей в <code>scada</code> тоже зависит от автомата</li>
</ul>
<p dir="auto">Например, если на линии нет второй камеры агрегации, то backend не должен ожидать наличие <code>CamAgregation2</code>, <code>Dev043...</code>, <code>Dev044...</code> и связанных с ними полей вроде <code>Dev043CounterGeneral</code> или <code>Dev044Error</code>. Аналогично, если на автомате только один принтер, то не будет ни <code>Printer12</code>, ни <code>LineDev012...</code>.</p>
<p dir="auto">Поэтому все примеры ниже надо читать так:</p>
<ul dir="auto">
<li>для <code>Grunw11</code> подтверждено именно в таком виде</li>
<li>для других автоматов backend обязан сначала определить фактический состав устройств, а уже потом строить topology, <code>DEVICES_STATUS</code>, <code>ALERT</code> и другие ответы</li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Главный вывод</h2><a id="user-content-главный-вывод" class="anchor" aria-label="Permalink: Главный вывод" href="#главный-вывод"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для текущего минимального фронтового API источники распадаются на четыре группы:</p>
<ol dir="auto">
<li>Статическая топология приложения.</li>
<li>Данные партии и очереди из <code>BatchQueue</code>.</li>
<li>Состояние линии из <code>Line</code>.</li>
<li>Диагностика камер и принтеров из <code>scada</code>, плюс прямые поля из <code>PrinterXX</code> и <code>Cam...</code>.</li>
</ol>
<p dir="auto">Практическое правило для backend:</p>
<ul dir="auto">
<li>все, что относится к партии, брать в первую очередь из <code>BatchQueue</code></li>
<li>все, что относится к line-level состоянию, брать из <code>Line</code></li>
<li>все, что относится к агрегированной диагностике камер и принтеров, брать из <code>scada</code></li>
<li>прямые поля принтеров и камер брать из их unit-файлов только там, где <code>scada</code> не содержит нужной информации</li>
</ul>
<p dir="auto">Но это правило работает только после шага discovery:</p>
<ul dir="auto">
<li>сначала backend определяет, какие устройства реально присутствуют у данного автомата</li>
<li>затем проверяет, какие агрегированные поля реально существуют для этого состава устройств</li>
<li>и только потом мапит их в frontend DTO</li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Подтвержденные backend-источники для Grunw11</h2><a id="user-content-подтвержденные-backend-источники-для-grunw11" class="anchor" aria-label="Permalink: Подтвержденные backend-источники для Grunw11" href="#подтвержденные-backend-источники-для-grunw11"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Ниже перечислено то, что подтверждено именно для <code>Grunw11</code>. Для другого автомата этот список может быть короче, длиннее или содержать устройства других типов.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Реальные unit-источники</h3><a id="user-content-реальные-unit-источники" class="anchor" aria-label="Permalink: Реальные unit-источники" href="#реальные-unit-источники"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li><code>BatchQueue</code></li>
<li><code>Line</code></li>
<li><code>scada</code></li>
<li><code>Printer11</code></li>
<li><code>Printer12</code></li>
<li><code>Printer13</code></li>
<li><code>Printer14</code></li>
<li><code>CamAgregation1</code></li>
<li><code>CamAgregation2</code></li>
<li><code>CamAgregationBox1</code></li>
<li><code>CamAgregationBox2</code></li>
<li><code>CamChecker1</code></li>
<li><code>CamChecker2</code></li>
<li><code>CamEanChecker1</code></li>
<li><code>CamEanChecker2</code></li>
<li><code>CamEanChecker3</code></li>
<li><code>CamEanChecker4</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Что делает <code>scada</code></h3><a id="user-content-что-делает-scada" class="anchor" aria-label="Permalink: Что делает scada" href="#что-делает-scada"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для <code>Grunw11</code> агрегатор <code>scada</code> генерирует для фронта подтвержденные диагностические теги:</p>
<ul dir="auto">
<li><code>Dev041...</code></li>
<li><code>Dev042...</code></li>
<li><code>Dev043...</code></li>
<li><code>Dev044...</code></li>
<li><code>Dev071...</code></li>
<li><code>Dev072...</code></li>
<li><code>Dev073...</code></li>
<li><code>Dev074...</code></li>
<li><code>LineDev011...</code></li>
<li><code>LineDev012...</code></li>
<li><code>LineDev013...</code></li>
<li><code>LineDev014...</code></li>
<li><code>lineerr</code></li>
</ul>
<p dir="auto">Важно:</p>
<ul dir="auto">
<li>это не универсальный список полей <code>scada</code> для всех автоматов</li>
<li>префиксы <code>Dev041...</code>, <code>Dev042...</code>, <code>Dev043...</code>, <code>Dev044...</code> появляются только там, где в конфигурации автомата реально есть соответствующие устройства и маппинг в <code>scada</code></li>
<li>то же самое относится к <code>LineDev011...</code>, <code>LineDev012...</code>, <code>LineDev013...</code>, <code>LineDev014...</code>: число этих групп зависит от числа принтеров на линии</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Маппинг устройств в SCADA-диагностику</h3><a id="user-content-маппинг-устройств-в-scada-диагностику" class="anchor" aria-label="Permalink: Маппинг устройств в SCADA-диагностику" href="#маппинг-устройств-в-scada-диагностику"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Камеры:</p>
<ul dir="auto">
<li><code>CamAgregation1 -&gt; Dev041</code></li>
<li><code>CamAgregationBox1 -&gt; Dev042</code></li>
<li><code>CamAgregation2 -&gt; Dev043</code></li>
<li><code>CamAgregationBox2 -&gt; Dev044</code></li>
<li><code>CamEanChecker1 -&gt; Dev071</code></li>
<li><code>CamEanChecker2 -&gt; Dev072</code></li>
<li><code>CamEanChecker3 -&gt; Dev073</code></li>
<li><code>CamEanChecker4 -&gt; Dev074</code></li>
</ul>
<p dir="auto">Принтеры:</p>
<ul dir="auto">
<li><code>Printer11 -&gt; LineDev011</code></li>
<li><code>Printer12 -&gt; LineDev012</code></li>
<li><code>Printer13 -&gt; LineDev013</code></li>
<li><code>Printer14 -&gt; LineDev014</code></li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">1. REST topology</h2><a id="user-content-1-rest-topology" class="anchor" aria-label="Permalink: 1. REST topology" href="#1-rest-topology"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">GET <code>/api/v1.0.0/workshops/topology</code></h3><a id="user-content-get-apiv100workshopstopology" class="anchor" aria-label="Permalink: GET /api/v1.0.0/workshops/topology" href="#get-apiv100workshopstopology"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li>не PrintSrv</li>
<li>backend-конфигурация приложения</li>
</ul>
<p dir="auto">Причина:</p>
<ul dir="auto">
<li>в анализе Grunw11 нет данных о цехах как о сущности верхнего уровня</li>
<li>PrintSrv знает устройство и линию, но не прикладную мобильную иерархию цехов</li>
</ul>
<p dir="auto">Рекомендация:</p>
<ul dir="auto">
<li>хранить <code>workshops</code> в YAML/БД backend</li>
</ul>
<p dir="auto">Поля:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Откуда брать</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>backend config</td>
</tr>
<tr>
<td><code>name</code></td>
<td>backend config</td>
</tr>
<tr>
<td><code>totalUnits</code></td>
<td>backend config или расчет по unit topology</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">GET <code>/api/v1.0.0/workshops/{workshopId}/units/topology</code></h3><a id="user-content-get-apiv100workshopsworkshopidunitstopology" class="anchor" aria-label="Permalink: GET /api/v1.0.0/workshops/{workshopId}/units/topology" href="#get-apiv100workshopsworkshopidunitstopology"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li>не PrintSrv напрямую</li>
<li>backend-конфигурация приложения</li>
</ul>
<p dir="auto">Поля:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Откуда брать</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>id</code></td>
<td>backend config</td>
</tr>
<tr>
<td><code>workshopId</code></td>
<td>backend config</td>
</tr>
<tr>
<td><code>unit</code></td>
<td>backend config</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">GET <code>/api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology</code></h3><a id="user-content-get-apiv100workshopsworkshopidunitsunitiddevicestopology" class="anchor" aria-label="Permalink: GET /api/v1.0.0/workshops/{workshopId}/units/{unitId}/devices/topology" href="#get-apiv100workshopsworkshopidunitsunitiddevicestopology"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li><code>Line___Unit0.xml</code></li>
<li>группировка по именам устройств</li>
</ul>
<p dir="auto">Для Grunw11 подтверждено:</p>
<ul dir="auto">
<li><code>Level1Printers = Printer11,Printer12,Printer13,Printer14</code></li>
<li><code>LineDevices = CamChecker1,CamChecker2,CamAgregation1,CamAgregation2,CamAgregationBox1,CamAgregationBox2,CamEanChecker1,CamEanChecker2,CamEanChecker3,CamEanChecker4,Printer11,Printer12,Printer13,Printer14</code></li>
</ul>
<p dir="auto">Рекомендуемая сборка ответа для Grunw11:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>devices.printers</code></td>
<td><code>Line.Level1Printers</code></td>
</tr>
<tr>
<td><code>devices.aggregationCams</code></td>
<td>фильтр <code>Line.LineDevices</code> по <code>CamAgregation1</code>, <code>CamAgregation2</code></td>
</tr>
<tr>
<td><code>devices.aggregationBoxCams</code></td>
<td>фильтр <code>Line.LineDevices</code> по <code>CamAgregationBox1</code>, <code>CamAgregationBox2</code></td>
</tr>
<tr>
<td><code>devices.checkerCams</code></td>
<td>фильтр <code>Line.LineDevices</code> по <code>CamChecker1</code>, <code>CamChecker2</code>, <code>CamEanChecker1..4</code></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Примечание:</p>
<ul dir="auto">
<li><code>CamEanChecker1..4</code> не имеют отдельной группы в текущем контракте frontend и логично попадают в <code>checkerCams</code></li>
<li>для другого аппарата backend не должен хардкодить этот набор; его нужно собирать из фактического <code>Line.LineDevices</code> и связанных unit-конфигов</li>
<li>актуальный контракт дополнен per-unit раскладкой устройств: <code>groups</code> (группы «машина»/«Поток N») и <code>deviceMeta</code> (имя на экране + признак счётчиков) настраиваются в админке per-unit (<code>unit_devices</code>: <code>display_name</code>, <code>group_label</code>, <code>display_order</code>, <code>show_counters</code>, <code>scada_prefix</code>). Основание и дефолты групп — <a href="/savushkin-dev/scada-mobile/blob/main/PRINTSRV_UI_MAP.md">PRINTSRV_UI_MAP.md</a></li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">2. WebSocket <code>/ws/unit/{unitId}</code></h2><a id="user-content-2-websocket-wsunitunitid" class="anchor" aria-label="Permalink: 2. WebSocket /ws/unit/{unitId}" href="#2-websocket-wsunitunitid"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>LINE_STATUS</code></h2><a id="user-content-line_status" class="anchor" aria-label="Permalink: LINE_STATUS" href="#line_status"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник для каждого поля:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Рекомендуемый источник</th>
<th>Комментарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>lineName</code></td>
<td>backend topology config</td>
<td>Лучше брать отображаемое имя аппарата, а не имя <code>.mpr</code> файла</td>
</tr>
<tr>
<td><code>lineState</code></td>
<td><code>Line.ST</code></td>
<td>Подтвержденный источник</td>
</tr>
<tr>
<td><code>shortCode</code></td>
<td><code>BatchQueue.kmc</code>, fallback <code>Printer11.kmc</code></td>
<td>В текущей документации это наиболее близкий подтвержденный источник короткого кода продукта</td>
</tr>
<tr>
<td><code>description</code></td>
<td><code>BatchQueue.description</code>, fallback <code>Printer11.descr</code></td>
<td>Для линии первичнее <code>BatchQueue</code>, т.к. это текущая партия</td>
</tr>
<tr>
<td><code>ean13</code></td>
<td><code>BatchQueue.ean13</code>, fallback <code>Printer11.ean13</code></td>
<td>Аналогично</td>
</tr>
<tr>
<td><code>batchNumber</code></td>
<td><code>BatchQueue.batch</code>, fallback <code>Printer11.partynumber</code></td>
<td><code>BatchQueue.batch</code> лучше отражает текущую партию линии</td>
</tr>
<tr>
<td><code>dateProduced</code></td>
<td><code>BatchQueue.dateproduced</code>, fallback <code>Printer11.dateproduced</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>datePacking</code></td>
<td><code>BatchQueue.datepack</code>, fallback <code>Printer11.datepack</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>dateExpiration</code></td>
<td><code>BatchQueue.dateexpiration</code>, fallback <code>Printer11.dateexpiration</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>initialCounter</code></td>
<td>требует уточнения, временно <code>Printer11.curitem</code> с парсингом первого сегмента</td>
<td>В Grunw11 нет отдельного подтвержденного поля именно начального счетчика; <code>BatchQueue.begincounter</code> это флаг, а не само числовое значение</td>
</tr>
<tr>
<td><code>site</code></td>
<td><code>BatchQueue.place</code>, fallback <code>Printer11.place</code></td>
<td>Семантически это место/слот на линии</td>
</tr>
<tr>
<td><code>itf</code></td>
<td><code>BatchQueue.itf</code>, fallback <code>Printer11.itf</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>capacity</code></td>
<td><code>BatchQueue.emk</code>, fallback <code>Printer11.emk</code></td>
<td>В документации подтверждено как количество единиц в коробе</td>
</tr>
<tr>
<td><code>boxCount</code></td>
<td>требует бизнес-уточнения, кандидаты <code>BatchQueue.kole</code> или <code>BatchQueue.kolm</code></td>
<td>По подтвержденной документации <code>kole</code> = количество единиц, <code>kolm</code> = количество маркировок; название <code>boxCount</code> не совпадает с семантикой 1:1</td>
</tr>
<tr>
<td><code>packageCount</code></td>
<td>требует бизнес-уточнения, кандидаты <code>BatchQueue.kolm</code> или <code>BatchQueue.kole</code></td>
<td>Аналогично</td>
</tr>
<tr>
<td><code>freeze</code></td>
<td><code>BatchQueue.frozen</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>region</code></td>
<td><code>BatchQueue.region</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>design</code></td>
<td><code>BatchQueue.designe</code>, fallback <code>Printer11.designe</code></td>
<td>Подтверждено</td>
</tr>
<tr>
<td><code>printDM</code></td>
<td><code>BatchQueue.printdm</code>, fallback <code>Printer11.printdm</code></td>
<td>Подтверждено</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Итог по <code>LINE_STATUS</code>:</p>
<ul dir="auto">
<li>почти весь блок можно собрать из <code>BatchQueue</code> + <code>Line</code></li>
<li><code>Printer11</code> нужен как fallback или как источник отдельных принтерных деталей</li>
<li>поля <code>initialCounter</code>, <code>boxCount</code>, <code>packageCount</code> сейчас семантически неоднозначны и должны быть подтверждены отдельно</li>
<li>наличие fallback-полей в <code>Printer11</code> тоже зависит от аппарата: на некоторых линиях принтеров может быть больше одного, а на некоторых только один</li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>DEVICES_STATUS</code></h2><a id="user-content-devices_status" class="anchor" aria-label="Permalink: DEVICES_STATUS" href="#devices_status"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Это самый аппаратозависимый блок во всем контракте. Здесь нельзя предполагать фиксированное количество устройств.</p>
<p dir="auto">Для одного аппарата массив <code>printers[]</code> может содержать один объект, для другого два, для третьего четыре и более. То же касается камер: где-то будут только <code>CamAgregation</code> и <code>CamChecker</code>, где-то добавятся <code>CamEanChecker1..4</code>, а где-то набор устройств будет вообще другой.</p>
<p dir="auto">Следствие для backend:</p>
<ul dir="auto">
<li>размеры массивов <code>printers</code>, <code>aggregationCams</code>, <code>aggregationBoxCams</code>, <code>checkerCams</code> должны определяться по topology конкретного аппарата</li>
<li>список полей <code>scada</code>, доступных для этих массивов, должен выводиться из фактического device-to-scada mapping, а не из предположения, что всегда существуют <code>Dev041...Dev044</code> или <code>LineDev011...LineDev014</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Принтеры</h3><a id="user-content-принтеры" class="anchor" aria-label="Permalink: Принтеры" href="#принтеры"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Рекомендуемая схема:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
<th>Комментарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>printers[].deviceName</code></td>
<td>topology</td>
<td><code>Printer11..14</code></td>
</tr>
<tr>
<td><code>printers[].state</code></td>
<td><code>PrinterXX.ST</code></td>
<td>Прямое состояние принтера</td>
</tr>
<tr>
<td><code>printers[].error</code></td>
<td><code>scada.LineDev0XXError</code></td>
<td>Это именно тот error-слой, который видит SCADA</td>
</tr>
<tr>
<td><code>printers[].batch</code></td>
<td>если нужен счетчик: <code>PrinterXX.kolm</code>; если нужна строка как в SCADA: <code>PrinterXX.curitem</code></td>
<td>В текущем фронтовом описании поле названо как счетчик, но в SCADA карточка принтера использует <code>curitem</code></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Практическая рекомендация:</p>
<ul dir="auto">
<li>если фронту нужен именно числовой счетчик, лучше отдать <code>PrinterXX.kolm</code></li>
<li>если фронту нужно то же, что в SCADA-блоке принтера, лучше переименовать поле и отдавать <code>PrinterXX.curitem</code></li>
<li>количество принтеров на линии брать не из имени <code>Printer11</code>, а из topology аппарата; на другой линии список может закончиться на <code>Printer11</code>, <code>Printer12</code> или иметь иной состав</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Камеры агрегации на потоке</h3><a id="user-content-камеры-агрегации-на-потоке" class="anchor" aria-label="Permalink: Камеры агрегации на потоке" href="#камеры-агрегации-на-потоке"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для Grunw11 это:</p>
<ul dir="auto">
<li><code>CamAgregation1</code></li>
<li><code>CamAgregation2</code></li>
</ul>
<p dir="auto">Рекомендуемая схема:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
<th>Пример для Grunw11</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>aggregationCams[].deviceName</code></td>
<td>topology</td>
<td><code>CamAgregation1</code>, <code>CamAgregation2</code></td>
</tr>
<tr>
<td><code>aggregationCams[].read</code></td>
<td><code>scada.Dev041CounterGeneral</code>, <code>scada.Dev043CounterGeneral</code></td>
<td>агрегированная статистика SCADA</td>
</tr>
<tr>
<td><code>aggregationCams[].unread</code></td>
<td><code>scada.Dev041CounterMissing</code>, <code>scada.Dev043CounterMissing</code></td>
<td>агрегированная статистика SCADA</td>
</tr>
<tr>
<td><code>aggregationCams[].state</code></td>
<td><code>scada.Dev041Work</code>, <code>scada.Dev043Work</code>, fallback direct <code>Cam...ST</code></td>
<td>для UI ближе именно <code>Work</code>-флаг из <code>scada</code></td>
</tr>
<tr>
<td><code>aggregationCams[].error</code></td>
<td><code>scada.Dev041Error</code>, <code>scada.Dev043Error</code></td>
<td>агрегированная ошибка</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Важно:</p>
<ul dir="auto">
<li>это пример именно для <code>Grunw11</code></li>
<li>на аппарате с одной камерой агрегации будет только одна группа полей такого типа</li>
<li>backend должен строить соответствие не по номерам <code>041</code> и <code>043</code>, а по реальному маппингу устройства в <code>scada</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Камеры агрегации на коробе</h3><a id="user-content-камеры-агрегации-на-коробе" class="anchor" aria-label="Permalink: Камеры агрегации на коробе" href="#камеры-агрегации-на-коробе"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для Grunw11 это:</p>
<ul dir="auto">
<li><code>CamAgregationBox1</code></li>
<li><code>CamAgregationBox2</code></li>
</ul>
<p dir="auto">Рекомендуемая схема:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
<th>Пример для Grunw11</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>aggregationBoxCams[].deviceName</code></td>
<td>topology</td>
<td><code>CamAgregationBox1</code>, <code>CamAgregationBox2</code></td>
</tr>
<tr>
<td><code>aggregationBoxCams[].read</code></td>
<td><code>scada.Dev042CounterGeneral</code>, <code>scada.Dev044CounterGeneral</code></td>
<td></td>
</tr>
<tr>
<td><code>aggregationBoxCams[].unread</code></td>
<td><code>scada.Dev042CounterMissing</code>, <code>scada.Dev044CounterMissing</code></td>
<td></td>
</tr>
<tr>
<td><code>aggregationBoxCams[].state</code></td>
<td><code>scada.Dev042Work</code>, <code>scada.Dev044Work</code>, fallback direct <code>Cam...ST</code></td>
<td></td>
</tr>
<tr>
<td><code>aggregationBoxCams[].error</code></td>
<td><code>scada.Dev042Error</code>, <code>scada.Dev044Error</code></td>
<td></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Важно:</p>
<ul dir="auto">
<li>на других аппаратах камеры коробочной агрегации могут отсутствовать совсем</li>
<li>соответственно, могут отсутствовать и группы полей <code>Dev042...</code> или <code>Dev044...</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Checker-камеры</h3><a id="user-content-checker-камеры" class="anchor" aria-label="Permalink: Checker-камеры" href="#checker-камеры"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для Grunw11 в <code>checkerCams</code> логично включать:</p>
<ul dir="auto">
<li><code>CamChecker1</code></li>
<li><code>CamChecker2</code></li>
<li><code>CamEanChecker1</code></li>
<li><code>CamEanChecker2</code></li>
<li><code>CamEanChecker3</code></li>
<li><code>CamEanChecker4</code></li>
</ul>
<p dir="auto">Но источник данных здесь неоднородный.</p>
<p dir="auto">И это еще один аппаратозависимый участок:</p>
<ul dir="auto">
<li>на части аппаратов есть только <code>CamChecker</code></li>
<li>на части есть и <code>CamChecker</code>, и <code>CamEanChecker1..4</code></li>
<li>на части аппаратов может не быть <code>CamEanChecker</code> вообще</li>
</ul>
<div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">EAN-checker камеры</h4><a id="user-content-ean-checker-камеры" class="anchor" aria-label="Permalink: EAN-checker камеры" href="#ean-checker-камеры"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для <code>CamEanChecker1..4</code> использовать тот же подход, что и для SCADA:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>deviceName</code></td>
<td>topology</td>
</tr>
<tr>
<td><code>read</code></td>
<td><code>scada.Dev071..074CounterGeneral</code></td>
</tr>
<tr>
<td><code>unread</code></td>
<td><code>scada.Dev071..074CounterMissing</code></td>
</tr>
<tr>
<td><code>state</code></td>
<td><code>scada.Dev071..074Work</code></td>
</tr>
<tr>
<td><code>error</code></td>
<td><code>scada.Dev071..074Error</code></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Но только если такие устройства вообще существуют на данном аппарате. Для аппаратов без <code>CamEanChecker1..4</code> этих полей в <code>scada</code> тоже не будет.</p>
<div class="markdown-heading" dir="auto"><h4 tabindex="-1" class="heading-element" dir="auto">CamChecker1 и CamChecker2</h4><a id="user-content-camchecker1-и-camchecker2" class="anchor" aria-label="Permalink: CamChecker1 и CamChecker2" href="#camchecker1-и-camchecker2"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для <code>CamChecker1/2</code> отдельного <code>Dev0XX</code>-слоя в <code>scada</code> для Grunw11 не подтверждено.</p>
<p dir="auto">Подтвержденные прямые поля в самих unit-файлах:</p>
<ul dir="auto">
<li><code>ST</code></li>
<li><code>BatchSucceeded</code></li>
<li><code>BatchFailed</code></li>
<li><code>message</code></li>
<li><code>LastReadTime</code></li>
</ul>
<p dir="auto">Рекомендуемая схема:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
<th>Комментарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>deviceName</code></td>
<td>topology</td>
<td></td>
</tr>
<tr>
<td><code>read</code></td>
<td><code>CamCheckerX.BatchSucceeded</code></td>
<td>прямой device-source</td>
</tr>
<tr>
<td><code>unread</code></td>
<td><code>CamCheckerX.BatchFailed</code></td>
<td>прямой device-source</td>
</tr>
<tr>
<td><code>state</code></td>
<td><code>CamCheckerX.ST</code></td>
<td>прямой device-source</td>
</tr>
<tr>
<td><code>error</code></td>
<td>backend-вычисление из runtime <code>err</code> или из alert-service</td>
<td>отдельного подтвержденного <code>scada.Dev...Error</code> для CamChecker1/2 нет</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Итог по <code>DEVICES_STATUS</code>:</p>
<ul dir="auto">
<li>для принтеров и камер с Dev-мэппингом основным источником диагностики должен быть <code>scada</code></li>
<li>для <code>CamChecker1/2</code> backend должен брать прямые счетчики из device-unit и сам вычислять error-флаг</li>
<li>количество объектов в каждом массиве и набор доступных <code>scada</code>-полей должны вычисляться динамически по конкретному аппарату</li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>QUEUE</code></h2><a id="user-content-queue" class="anchor" aria-label="Permalink: QUEUE" href="#queue"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li><code>BatchQueue</code></li>
<li>при возможности лучше опираться на структурированное представление очереди, а не только на display-строки</li>
</ul>
<p dir="auto">Подтвержденные поля:</p>
<ul dir="auto">
<li><code>Item01..Item10</code></li>
<li><code>Item01Selected..Item10Selected</code></li>
<li><code>CurItem</code></li>
</ul>
<p dir="auto">Дополнительный источник, если backend умеет читать его отдельно:</p>
<ul dir="auto">
<li><code>Config/Default/Files/BatchQueue.json</code></li>
</ul>
<p dir="auto">Рекомендуемая схема:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
<th>Комментарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>items[].position</code></td>
<td>индекс <code>Item01..Item10</code></td>
<td>подтверждено</td>
</tr>
<tr>
<td><code>items[].shortCode</code></td>
<td>предпочтительно структурированный парсинг из <code>BatchQueue.json</code>; fallback парсинг <code>ItemXX</code></td>
<td><code>ItemXX</code> это display-строка, формат может быть нестрогим</td>
</tr>
<tr>
<td><code>items[].batch</code></td>
<td>предпочтительно <code>BatchQueue.json</code>; fallback парсинг <code>ItemXX</code></td>
<td></td>
</tr>
<tr>
<td><code>items[].dateProduced</code></td>
<td>предпочтительно <code>BatchQueue.json</code>; fallback парсинг <code>ItemXX</code></td>
<td></td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Практическая рекомендация:</p>
<ul dir="auto">
<li>если backend имеет доступ к <code>BatchQueue.json</code>, это более надежный источник для <code>QUEUE</code>, чем парсинг строк <code>Item01..Item10</code></li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>ERRORS</code></h2><a id="user-content-errors" class="anchor" aria-label="Permalink: ERRORS" href="#errors"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Этот блок не собирается из одного XML-файла.</p>
<p dir="auto">Он требует backend-агрегации из нескольких слоев:</p>
<ol dir="auto">
<li>runtime-состояние устройств</li>
<li><code>scada</code>-теги ошибок</li>
<li>состояние линии</li>
<li>журнал событий</li>
</ol>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>deviceErrors[]</code></h3><a id="user-content-deviceerrors" class="anchor" aria-label="Permalink: deviceErrors[]" href="#deviceerrors"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Рекомендуемые источники:</p>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Откуда брать</th>
<th>Комментарий</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>objectName</code></td>
<td>реальное device-name: <code>Printer11</code>, <code>CamAgregation1</code>, <code>Line</code> и т.д.</td>
<td>лучше использовать backend-имя устройства, не SCADA-имя виджета</td>
</tr>
<tr>
<td><code>propertyDesc</code></td>
<td>backend mapping table по флагу ошибки</td>
<td>например <code>LineDev011Error</code>, <code>Dev041Dublicate</code>, <code>Dev071Fail</code></td>
</tr>
<tr>
<td><code>value</code></td>
<td>текущее значение error-флага</td>
<td>обычно <code>1</code>/<code>0</code></td>
</tr>
<tr>
<td><code>description</code></td>
<td>backend human-readable message</td>
<td>из словаря ошибок или описания состояния</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<p dir="auto">Для Grunw11 подтвержденные error-источники:</p>
<ul dir="auto">
<li><code>scada.LineDev011Error..LineDev014Error</code></li>
<li><code>scada.Dev041Error..Dev044Error</code></li>
<li><code>scada.Dev071Error..Dev074Error</code></li>
<li><code>Line.Error</code></li>
<li><code>Line.ErrorMessage</code></li>
</ul>
<p dir="auto">Для других аппаратов этот список должен пересчитываться по фактическому составу устройств. Нельзя считать, что если в коде для <code>Grunw11</code> есть <code>Dev041Error</code> или <code>LineDev014Error</code>, то такие же поля будут у любой другой линии.</p>
<p dir="auto">Для <code>CamChecker1/2</code> error-флаг нужно вычислять backend-ом из runtime-состояния камеры, так как отдельного <code>Dev</code>-агрегата не подтверждено.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>logs[]</code></h3><a id="user-content-logs" class="anchor" aria-label="Permalink: logs[]" href="#logs"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">По анализу Grunw11 и существующей документации наиболее реалистичные источники:</p>
<ul dir="auto">
<li><code>sqlite_logs.db</code>, если backend использует его как журнал событий</li>
<li>либо файлы логов PrintSrv, если backend читает их напрямую</li>
</ul>
<p dir="auto">На уровне минимального фронтового контракта это backend-агрегируемый слой, а не поле из одного unit.</p>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">3. WebSocket <code>/ws/live</code></h2><a id="user-content-3-websocket-wslive" class="anchor" aria-label="Permalink: 3. WebSocket /ws/live" href="#3-websocket-wslive"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>UNITS_STATUS</code></h2><a id="user-content-units_status" class="anchor" aria-label="Permalink: UNITS_STATUS" href="#units_status"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>payload[].event</code></h3><a id="user-content-payloadevent" class="anchor" aria-label="Permalink: payload[].event" href="#payloadevent"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Не существует как готовое поле в Grunw11 unit-файлах.</p>
<p dir="auto">Это backend-производное представление состояния аппарата.</p>
<p dir="auto">Рекомендуемые входы для вычисления:</p>
<ul dir="auto">
<li><code>Line.ST</code></li>
<li><code>Line.Error</code></li>
<li><code>Line.ErrorMessage</code></li>
<li><code>scada.lineerr</code></li>
</ul>
<p dir="auto">Пример логики:</p>
<ul dir="auto">
<li>если есть ошибка линии или активные device-ошибки, отдавать текст ошибки</li>
<li>если <code>Line.ST = 1</code>, отдавать состояние типа <code>В работе</code></li>
<li>если <code>Line.ST != 1</code> и ошибок нет, отдавать <code>Стоп</code> или аналогичный warning-state</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>payload[].timer</code></h3><a id="user-content-payloadtimer" class="anchor" aria-label="Permalink: payload[].timer" href="#payloadtimer"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для Grunw11 в проанализированных PrintSrv-данных подтвержденного прямого поля таймера состояния не найдено.</p>
<p dir="auto">Рекомендация:</p>
<ul dir="auto">
<li>вычислять backend-ом как время с момента последней смены вычисленного <code>event</code></li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto"><code>ALERT_SNAPSHOT</code> и <code>ALERT</code></h2><a id="user-content-alert_snapshot-и-alert" class="anchor" aria-label="Permalink: ALERT_SNAPSHOT и ALERT" href="#alert_snapshot-и-alert"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Это не прямой снимок одного устройства, а результат backend-агрегации.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Какие поля можно брать</h3><a id="user-content-какие-поля-можно-брать" class="anchor" aria-label="Permalink: Какие поля можно брать" href="#какие-поля-можно-брать"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<markdown-accessiblity-table><table>
<thead>
<tr>
<th>Поле API</th>
<th>Источник</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>workshopId</code></td>
<td>backend topology config</td>
</tr>
<tr>
<td><code>unitId</code></td>
<td>backend topology config</td>
</tr>
<tr>
<td><code>unitName</code></td>
<td>backend topology config</td>
</tr>
<tr>
<td><code>active</code></td>
<td>backend alert engine</td>
</tr>
<tr>
<td><code>timestamp</code></td>
<td>backend alert engine</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Из чего собирать сами алерты</h3><a id="user-content-из-чего-собирать-сами-алерты" class="anchor" aria-label="Permalink: Из чего собирать сами алерты" href="#из-чего-собирать-сами-алерты"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для Grunw11 подтвержденные источники для alert engine:</p>
<ul dir="auto">
<li><code>Line.Error</code></li>
<li><code>Line.ErrorMessage</code></li>
<li><code>Line.ST</code></li>
<li><code>scada.lineerr</code></li>
<li><code>scada.LineDev011Error..014Error</code></li>
<li><code>scada.Dev041Error..044Error</code></li>
<li><code>scada.Dev071Error..074Error</code></li>
<li>runtime <code>err</code> для устройств без <code>scada</code>-обертки, например <code>CamChecker1/2</code></li>
</ul>
<p dir="auto">Для других аппаратов backend должен собирать список alert-источников по фактическим unit-объектам и реально существующим <code>scada</code>-полям, а не по фиксированному шаблону <code>Grunw11</code>.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>errors[].device</code></h3><a id="user-content-errorsdevice" class="anchor" aria-label="Permalink: errors[].device" href="#errorsdevice"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Рекомендуется отдавать реальные backend-имена устройств:</p>
<ul dir="auto">
<li><code>Printer11</code></li>
<li><code>Printer12</code></li>
<li><code>CamAgregation1</code></li>
<li><code>CamEanChecker1</code></li>
<li><code>Line</code></li>
</ul>
<p dir="auto">Не рекомендуется отдавать:</p>
<ul dir="auto">
<li><code>ConnectionPrinter11</code></li>
<li><code>CMSDev041</code></li>
<li><code>cdev41err</code></li>
</ul>
<p dir="auto">Причина:</p>
<ul dir="auto">
<li>это экранные объекты SCADA, а не реальные backend-устройства</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>errors[].code</code></h3><a id="user-content-errorscode" class="anchor" aria-label="Permalink: errors[].code" href="#errorscode"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li><code>Line.Error</code> или device-specific <code>message</code>, если есть код ошибки</li>
<li>если сообщение warning-класса и числового кода нет, можно отдавать <code>0</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>errors[].message</code></h3><a id="user-content-errorsmessage" class="anchor" aria-label="Permalink: errors[].message" href="#errorsmessage"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Источник:</p>
<ul dir="auto">
<li><code>Line.ErrorMessage</code>, если ошибка относится к линии</li>
<li>backend-словарь интерпретации кодов device/message-флагов</li>
<li>fallback: человекочитаемое описание сформированного alert-condition</li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto"><code>severity</code></h3><a id="user-content-severity" class="anchor" aria-label="Permalink: severity" href="#severity"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Это backend-вычисляемое поле.</p>
<p dir="auto">Под текущую модель Grunw11 логично:</p>
<ul dir="auto">
<li><code>Critical</code>, если есть активная ошибка линии или устройства</li>
<li><code>Warning</code>, если линия не работает, но явной ошибки нет</li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">4. Что можно брать уже сейчас без спорных мест</h2><a id="user-content-4-что-можно-брать-уже-сейчас-без-спорных-мест" class="anchor" aria-label="Permalink: 4. Что можно брать уже сейчас без спорных мест" href="#4-что-можно-брать-уже-сейчас-без-спорных-мест"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Ниже список полей, для которых источник по Grunw11 подтвержден достаточно уверенно.</p>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Можно забирать сразу</h3><a id="user-content-можно-забирать-сразу" class="anchor" aria-label="Permalink: Можно забирать сразу" href="#можно-забирать-сразу"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Ниже список полей, которые для <code>Grunw11</code> подтверждены уверенно. Этот блок не означает, что те же самые поля обязательно существуют у любого другого аппарата в том же объеме.</p>
<ul dir="auto">
<li><code>lineState -&gt; Line.ST</code></li>
<li><code>description -&gt; BatchQueue.description</code></li>
<li><code>ean13 -&gt; BatchQueue.ean13</code></li>
<li><code>batchNumber -&gt; BatchQueue.batch</code></li>
<li><code>dateProduced -&gt; BatchQueue.dateproduced</code></li>
<li><code>datePacking -&gt; BatchQueue.datepack</code></li>
<li><code>dateExpiration -&gt; BatchQueue.dateexpiration</code></li>
<li><code>site -&gt; BatchQueue.place</code></li>
<li><code>itf -&gt; BatchQueue.itf</code></li>
<li><code>capacity -&gt; BatchQueue.emk</code></li>
<li><code>freeze -&gt; BatchQueue.frozen</code></li>
<li><code>region -&gt; BatchQueue.region</code></li>
<li><code>design -&gt; BatchQueue.designe</code></li>
<li><code>printDM -&gt; BatchQueue.printdm</code></li>
<li><code>printers[].state -&gt; PrinterXX.ST</code></li>
<li><code>printers[].error -&gt; scada.LineDev0XXError</code></li>
<li><code>aggregationCams[].read/unread/error -&gt; scada.Dev041/043...</code></li>
<li><code>aggregationBoxCams[].read/unread/error -&gt; scada.Dev042/044...</code></li>
<li><code>ean-checker state/read/unread/error -&gt; scada.Dev071..074...</code></li>
</ul>
<div class="markdown-heading" dir="auto"><h3 tabindex="-1" class="heading-element" dir="auto">Требуют отдельного согласования</h3><a id="user-content-требуют-отдельного-согласования" class="anchor" aria-label="Permalink: Требуют отдельного согласования" href="#требуют-отдельного-согласования"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<ul dir="auto">
<li><code>initialCounter</code></li>
<li><code>boxCount</code></li>
<li><code>packageCount</code></li>
<li><code>UNITS_STATUS.payload[].event</code></li>
<li><code>UNITS_STATUS.payload[].timer</code></li>
<li><code>CamChecker1/2.error</code></li>
<li>структура <code>QUEUE.items[]</code>, если не читать <code>BatchQueue.json</code></li>
</ul>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Итоговая рекомендация для backend</h2><a id="user-content-итоговая-рекомендация-для-backend" class="anchor" aria-label="Permalink: Итоговая рекомендация для backend" href="#итоговая-рекомендация-для-backend"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Для минимального frontend-контракта backend должен строить ответ из следующих базовых источников:</p>
<ol dir="auto">
<li>topology config приложения</li>
<li><code>BatchQueue</code></li>
<li><code>Line</code></li>
<li><code>scada</code></li>
<li><code>Printer11..14</code></li>
<li><code>CamAgregation1..2</code></li>
<li><code>CamAgregationBox1..2</code></li>
<li><code>CamChecker1..2</code></li>
<li><code>CamEanChecker1..4</code></li>
</ol>
<p dir="auto">Но это перечень для <code>Grunw11</code>, а не для всех аппаратов. В общем случае правильный порядок работы такой:</p>
<ol dir="auto">
<li>определить состав устройств конкретного аппарата</li>
<li>определить, какие unit-объекты реально существуют в PrintSrv для этого аппарата</li>
<li>определить, какие агрегированные поля реально публикует <code>scada</code> для этого состава устройств</li>
<li>только после этого собирать frontend DTO</li>
</ol>
<p dir="auto">И отдельно должен иметь собственный слой агрегации для:</p>
<ol dir="auto">
<li><code>ALERT</code> и <code>ALERT_SNAPSHOT</code></li>
<li><code>UNITS_STATUS.event</code></li>
<li><code>UNITS_STATUS.timer</code></li>
<li><code>ERRORS.deviceErrors[]</code></li>
<li><code>QUEUE.items[]</code>, если нужна не display-строка, а структурированная очередь</li>
</ol>
<hr>
<div class="markdown-heading" dir="auto"><h2 tabindex="-1" class="heading-element" dir="auto">Короткий практический вывод</h2><a id="user-content-короткий-практический-вывод" class="anchor" aria-label="Permalink: Короткий практический вывод" href="#короткий-практический-вывод"><svg data-component="Octicon" class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path d="m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z"></path></svg></a></div>
<p dir="auto">Если смотреть только на то, что подтверждено по Grunw11, то frontend сейчас лучше всего кормить так:</p>
<ul dir="auto">
<li>карточку партии собирать в первую очередь из <code>BatchQueue</code></li>
<li>диагностику устройств брать из <code>scada</code></li>
<li>прямые статусы принтеров брать из <code>PrinterXX</code></li>
<li>прямые статусы <code>CamChecker1/2</code>, где нет <code>Dev</code>-обертки, брать из самих camera-unit</li>
<li>все человекочитаемые event/alert/log представления вычислять в backend, а не пытаться читать как готовые поля PrintSrv</li>
</ul>
<p dir="auto">И главное уточнение: это не означает, что на каждом аппарате обязательно будут все эти сущности и все эти поля. У одного аппарата может быть один принтер и одна камера, у другого два принтера и четыре EAN-checker камеры, у третьего иной состав устройств. Поэтому backend должен опираться на discovery конкретного аппарата, а не на фиксированный список полей из <code>Grunw11</code>.</p>
</article></div><div class="d-none"></div></section></div></div></div> </div> <!-- --> </div></div></div></div></div></div><div class="ScrollMarksContainer-module__scrollMarksContainer__Eu7uU" id="find-result-marks-container"></div><div class="d-none"></div><div class="d-none"></div></div> <!-- --> <!-- --> </div>
</react-app>




  </div>

</turbo-frame>

    </main>
  </div>

  </div>

          <footer class="footer f6 color-fg-muted color-border-subtle tmp-pt-7 tmp-pb-6 p-responsive" role="contentinfo"  >
  <h2 class='sr-only'>Footer</h2>

  


  <div class="d-flex flex-justify-center flex-items-center flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap">
    <div class="d-flex flex-items-center flex-shrink-0 mx-2">
      <a aria-label="GitHub Homepage" class="footer-octicon mr-2" href="https://github.com">
        <svg aria-hidden="true" data-component="Octicon" height="24" viewBox="0 0 24 24" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github">
    <path d="M10.226 17.284c-2.965-.36-5.054-2.493-5.054-5.256 0-1.123.404-2.336 1.078-3.144-.292-.741-.247-2.314.09-2.965.898-.112 2.111.36 2.83 1.01.853-.269 1.752-.404 2.853-.404 1.1 0 1.999.135 2.807.382.696-.629 1.932-1.1 2.83-.988.315.606.36 2.179.067 2.942.72.854 1.101 2 1.101 3.167 0 2.763-2.089 4.852-5.098 5.234.763.494 1.28 1.572 1.28 2.807v2.336c0 .674.561 1.056 1.235.786 4.066-1.55 7.255-5.615 7.255-10.646C23.5 6.188 18.334 1 11.978 1 5.62 1 .5 6.188.5 12.545c0 4.986 3.167 9.12 7.435 10.669.606.225 1.19-.18 1.19-.786V20.63a2.9 2.9 0 0 1-1.078.224c-1.483 0-2.359-.808-2.987-2.313-.247-.607-.517-.966-1.034-1.033-.27-.023-.359-.135-.359-.27 0-.27.45-.471.898-.471.652 0 1.213.404 1.797 1.235.45.651.921.943 1.483.943.561 0 .92-.202 1.437-.719.382-.381.674-.718.944-.943"></path>
</svg>
</a>
      <span>
        &copy; 2026 GitHub,&nbsp;Inc.
      </span>
    </div>

    <nav aria-label="Footer">
      <h3 class="sr-only" id="sr-footer-heading">Footer navigation</h3>

      <ul class="list-style-none d-flex flex-justify-center flex-wrap mb-2 mb-lg-0" aria-labelledby="sr-footer-heading">


          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to Terms&quot;,&quot;label&quot;:&quot;text:terms&quot;}" href="https://docs.github.com/site-policy/github-terms/github-terms-of-service" data-view-component="true" class="Link--secondary Link">Terms</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to privacy&quot;,&quot;label&quot;:&quot;text:privacy&quot;}" href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-view-component="true" class="Link--secondary Link">Privacy</a>
          </li>


            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to security&quot;,&quot;label&quot;:&quot;text:security&quot;}" href="https://github.com/security" data-view-component="true" class="Link--secondary Link">Security</a>
            </li>

            <li class="mx-2">
              <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to status&quot;,&quot;label&quot;:&quot;text:status&quot;}" href="https://www.githubstatus.com/" data-view-component="true" class="Link--secondary Link">Status</a>
            </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to community&quot;,&quot;label&quot;:&quot;text:community&quot;}" href="https://github.community/" data-view-component="true" class="Link--secondary Link">Community</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to docs&quot;,&quot;label&quot;:&quot;text:docs&quot;}" href="https://docs.github.com/" data-view-component="true" class="Link--secondary Link">Docs</a>
          </li>

          <li class="mx-2">
            <a data-analytics-event="{&quot;category&quot;:&quot;Footer&quot;,&quot;action&quot;:&quot;go to contact&quot;,&quot;label&quot;:&quot;text:contact&quot;}" href="https://support.github.com?tags=dotcom-footer" data-view-component="true" class="Link--secondary Link">Contact</a>
          </li>

          
<li class="mx-2" >
  <cookie-consent-link>
    <button
      type="button"
      class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent"
      data-action="click:cookie-consent-link#showConsentManagement"
      data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;cookies&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;cookies_link_subfooter_footer&quot;}"
    >
      Manage cookies
    </button>
  </cookie-consent-link>
</li>

  <li class="mx-2">
    <cookie-consent-link>
      <button
        type="button"
        class="Link--secondary underline-on-hover border-0 p-0 color-bg-transparent text-left"
        data-action="click:cookie-consent-link#showConsentManagement"
        data-analytics-event="{&quot;location&quot;:&quot;footer&quot;,&quot;action&quot;:&quot;dont_share_info&quot;,&quot;context&quot;:&quot;subfooter&quot;,&quot;tag&quot;:&quot;link&quot;,&quot;label&quot;:&quot;dont_share_info_link_subfooter_footer&quot;}"
      >
        Do not share my personal information
      </button>
    </cookie-consent-link>
  </li>

      </ul>
    </nav>
  </div>
</footer>



    <ghcc-consent id="ghcc" class="position-fixed bottom-0 left-0" style="z-index: 999999"
      data-locale="en"
      data-initial-cookie-consent-allowed=""
      data-cookie-consent-required="true"
    ></ghcc-consent>




  <div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden>
    <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
    <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
</svg>
    <button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
    </button>
    You can’t perform that action at this time.
  </div>

    <template id="site-details-dialog">
  <details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open>
    <summary role="button" aria-label="Close dialog"></summary>
    <details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
      <button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
        <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
    <path d="M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734L9.06 8l3.22 3.22a.749.749 0 0 1-.326 1.275.749.749 0 0 1-.734-.215L8 9.06l-3.22 3.22a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"></path>
</svg>
      </button>
      <div class="octocat-spinner tmp-my-6 js-details-dialog-spinner"></div>
    </details-dialog>
  </details>
</template>

    <div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;">
  <div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;">
  </div>
</div>

    <template id="snippet-clipboard-copy-button">
  <div class="zeroclipboard-container position-absolute right-0 top-0">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn js-clipboard-copy m-2 p-0" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2 tmp-m-2">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2 tmp-m-2">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>
<template id="snippet-clipboard-copy-button-unpositioned">
  <div class="zeroclipboard-container">
    <clipboard-copy aria-label="Copy code to clipboard" class="ClipboardButton btn btn-invisible js-clipboard-copy m-2 p-0 d-flex flex-justify-center flex-items-center" data-copy-feedback="Copied!" data-tooltip-direction="w">
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon">
    <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
      <svg aria-hidden="true" data-component="Octicon" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none">
    <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
    </clipboard-copy>
  </div>
</template>




    </div>
    <div id="js-global-screen-reader-notice" class="sr-only mt-n1" aria-live="polite" aria-atomic="true" ></div>
    <div id="js-global-screen-reader-notice-assertive" class="sr-only mt-n1" aria-live="assertive" aria-atomic="true"></div>
  </body>
</html>

