?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE: Job Management Job Categorization Interfaces' ??
MODULE qfm$job_categorization_manager;

{ PURPOSE:
{   This module contains the ring 1 Job Management job categorization
{   interfaces.  The interfaces are used to determine the categories
{   for a job and to assign a job to a particular job class.

?? NEWTITLE := 'Global Declarations Referenced by this Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc dft$family_access
*copyc dft$family_table_client_entry
*copyc jmc$class_names
*copyc jmc$job_management_id
*copyc jme$job_categorization_errors
*copyc jmt$job_class
*copyc jmt$job_class_list
*copyc jmt$job_system_label
*copyc jmt$maximum_mainframes
*copyc jmt$valid_mainframe_set
*copyc oss$mainframe_paged_literal
*copyc ost$name
*copyc ost$status
*copyc pmt$family_name_count
?? POP ??
*copyc osp$clear_mainframe_sig_lock
*copyc osp$decrement_locked_variable
*copyc osp$increment_locked_variable
*copyc osp$set_mainframe_sig_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc jmv$job_category_data
*copyc jmv$job_class_table_p
*copyc jmv$job_scheduler_table
*copyc jmv$scheduler_tables_access
*copyc osv$family_table
?? OLDTITLE ??
?? NEWTITLE := 'categories_fit', EJECT ??

{ PURPOSE
{   This function determines if the job categories fit with the required and
{   excluded categories provided.  This has been made into an inline function
{   to make the algorithm more readable and to ensure that the test is done
{   the same each time.

  FUNCTION [INLINE] categories_fit
    (    job_categories: jmt$job_category_set;
         required_categories: jmt$job_category_set;
         excluded_categories: jmt$job_category_set): boolean;

    categories_fit := (job_categories * required_categories = required_categories) AND
          (job_categories * excluded_categories = $jmt$job_category_set []);
  FUNCEND categories_fit;
?? OLDTITLE ??
?? NEWTITLE := 'determine_categories', EJECT ??

  PROCEDURE determine_categories
    (VAR {input, output} system_label: jmt$job_system_label;
     VAR status: ost$status);

    VAR
      category_kind_sets: jmt$job_category_set_list,
      item_kind: jmt$job_category_item_kind,
      job_categories: jmt$job_category_set,
      qualifier_index: 1 .. jmc$maximum_job_qualifiers,
      next_item: jmt$job_category_reference,
      item: ^jmt$job_category_item,
      first_item: ^jmt$job_category_item,
      numbers: array [jmt$job_category_item_kind] of integer,
      names: array [jmt$job_category_item_kind] of ost$name;

    VAR
      job_mode: [STATIC, READ, oss$mainframe_paged_literal] array [jmt$job_mode] of
            ost$name := ['BATCH', REP 4 of 'INTERACTIVE'];

    status.normal := TRUE;
    IF jmv$job_category_data.item_list = NIL THEN
      system_label.job_category_set := $jmt$job_category_set [];
      RETURN;
    IFEND;

    numbers [jmc$ca_cpu_time_limit] := system_label.limit_information.cpu_time_limit_assigned;
    IF numbers [jmc$ca_cpu_time_limit] = jmc$unlimited_cpu_time_limit THEN
      numbers [jmc$ca_cpu_time_limit] := jmc$highest_cpu_time_limit + jmc$unlimited_offset;
    IFEND;
    numbers [jmc$ca_sru_time_limit] := system_label.limit_information.sru_limit_assigned;
    IF numbers [jmc$ca_sru_time_limit] = jmc$unlimited_sru_limit THEN
      numbers [jmc$ca_sru_time_limit] := jmc$highest_sru_limit + jmc$unlimited_offset;
    IFEND;
    numbers [jmc$ca_mag_tape_limit] := system_label.limit_information.magnetic_tape_limit_assigned;
    numbers [jmc$ca_working_set] := system_label.limit_information.maximum_working_set_assigned;

    names [jmc$ca_login_account] := system_label.login_account;
    names [jmc$ca_login_project] := system_label.login_project;
    names [jmc$ca_login_family] := system_label.login_user_identification.family;
    names [jmc$ca_login_user] := system_label.login_user_identification.user;
    names [jmc$ca_user_job_name] := system_label.user_job_name;

    names [jmc$ca_orig_application_name] := system_label.job_attributes.originating_application_name;

    names [jmc$ca_job_mode] := job_mode [system_label.job_mode];

    first_item := #LOC (jmv$job_category_data.item_list^);
    item := first_item;
    category_kind_sets := jmv$job_category_data.initial_set_values;

  /matching/
    REPEAT
      next_item := item^.next_item;
      item_kind := item^.kind;
      IF item_kind <= jmc$ca_working_set THEN
        IF numbers [item_kind] <= item^.number THEN
          category_kind_sets [item_kind] := category_kind_sets [item_kind] + item^.categories;
          next_item := item^.skip_item;
        IFEND;

      ELSEIF item_kind < jmc$ca_job_qualifier THEN
        IF names [item_kind] <= item^.name THEN
          IF names [item_kind] = item^.name THEN
            category_kind_sets [item_kind] := category_kind_sets [item_kind] + item^.categories;
          IFEND;
          next_item := item^.skip_item;
        IFEND;

      ELSEIF item_kind = jmc$ca_job_qualifier THEN

{ jme$job_qualifier_not_valid

      /qualifier/
        FOR qualifier_index := 1 TO jmc$maximum_job_qualifiers DO
          IF item^.name = system_label.job_attributes.job_qualifier_list [qualifier_index] THEN
            category_kind_sets [jmc$ca_job_qualifier] := category_kind_sets [jmc$ca_job_qualifier] +
                  item^.categories;
            EXIT /qualifier/
          IFEND;
        FOREND /qualifier/;

      ELSE {item_kind = jmc$ca_ca_or_conditions }
        EXIT /matching/;
      IFEND;

      item := #PTR (next_item, jmv$job_category_data.item_list^);
    UNTIL item = first_item;

    job_categories := category_kind_sets [jmc$ca_cpu_time_limit];
    FOR item_kind := jmc$ca_sru_time_limit TO jmc$ca_job_qualifier DO
      job_categories := job_categories * category_kind_sets [item_kind];
    FOREND;

    WHILE item <> first_item DO
      IF (job_categories * item^.members) <> $jmt$job_category_set [] THEN
        job_categories := job_categories + item^.categories;
      IFEND;
      item := #PTR (item^.next_item, jmv$job_category_data.item_list^);
    WHILEND;

    system_label.job_category_set := job_categories;

  PROCEND determine_categories;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] qfp$categorize_job', EJECT ??
*copy qfh$categorize_job

  PROCEDURE [XDCL, #GATE] qfp$categorize_job
    (    valid_job_classes: jmt$job_class_list;
         number_of_valid_job_classes: ost$non_negative_integers;
     VAR system_label { input, output } : jmt$job_system_label;
     VAR assigned_job_class: jmt$job_class;
     VAR status: ost$status);

    VAR
      job_categories: jmt$job_category_set;


    VAR
      i: integer,
      actual: integer,
      lock_error: boolean,
      job_class: jmt$job_class;

    status.normal := TRUE;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);
    osp$increment_locked_variable (jmv$scheduler_tables_access.count, 0, actual);
    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

    determine_categories (system_label, status);
    job_categories := system_label.job_category_set;

  /find_job_class/
    BEGIN
      IF system_label.assigned_job_class = jmc$automatic_class_name THEN

        job_class := jmv$job_class_table_p^ [jmc$unassigned_job_class].next_rank_class;
        WHILE job_class <> jmc$null_job_class DO
          IF categories_fit (job_categories, jmv$job_class_table_p^ [job_class].required_categories,
                jmv$job_class_table_p^ [job_class].excluded_categories) THEN
            FOR i := 1 TO number_of_valid_job_classes DO
              IF (valid_job_classes [i] = jmv$job_class_table_p^ [job_class].name) OR
                    (valid_job_classes [i] = jmc$all_class_name) THEN
                system_label.assigned_job_class := jmv$job_class_table_p^ [job_class].name;
                assigned_job_class := job_class;
                EXIT /find_job_class/;
              IFEND;
            FOREND;
          IFEND;
          job_class := jmv$job_class_table_p^ [job_class].next_rank_class;
        WHILEND;
        osp$set_status_condition (jme$no_job_class_found_for_job, status);

      ELSE {Job class is given explicitly}

        FOR job_class := 1 TO UPPERBOUND (jmv$job_class_table_p^) DO
          IF jmv$job_class_table_p^ [job_class].defined THEN
            IF jmv$job_class_table_p^ [job_class].name = system_label.assigned_job_class THEN
              IF categories_fit (job_categories, jmv$job_class_table_p^ [job_class].required_categories,
                    jmv$job_class_table_p^ [job_class].excluded_categories) THEN
                assigned_job_class := job_class;
              ELSE
                osp$set_status_abnormal (jmc$job_management_id, jme$cannot_assign_to_job_class,
                      system_label.assigned_job_class, status);
              IFEND;
              EXIT /find_job_class/;
            IFEND;
          IFEND;
        FOREND;
        osp$set_status_abnormal (jmc$job_management_id, jme$job_class_does_not_exist,
              system_label.assigned_job_class, status);
      IFEND;
    END /find_job_class/;

    osp$decrement_locked_variable (jmv$scheduler_tables_access.count, actual, actual, lock_error);

  PROCEND qfp$categorize_job;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] qfp$check_for_profile_mismatch', EJECT ??
*copyc qfh$check_for_profile_mismatch

  PROCEDURE [XDCL, #GATE] qfp$check_for_profile_mismatch
    (    profile_version: ost$name;
     VAR profile_mismatch: boolean);

    profile_mismatch := jmv$job_scheduler_table.profile_identification <> profile_version;

  PROCEND qfp$check_for_profile_mismatch;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] qfp$determine_mainframe_fitness', EJECT ??
*copy qfh$determine_mainframe_fitness

  PROCEDURE [XDCL, #GATE] qfp$determine_mainframe_fitness
    (    job_category_set: jmt$job_category_set;
         leveled_job: boolean;
         login_family: ost$name;
     VAR valid_mainframes_set: jmt$valid_mainframe_set;
     VAR status: ost$status);

    VAR
      actual: integer,
      family_access: dft$family_access,
      family_mainframe_list_p: ^dft$family_table_client_entry,
      family_table_index: pmt$family_name_count,
      fits_a_mainframe: boolean,
      lock_error: boolean,
      login_family_index: pmt$family_name_count,
      mainframe_index: jmt$maximum_mainframes;

    osp$set_mainframe_sig_lock (jmv$scheduler_tables_access.lock);
    osp$increment_locked_variable (jmv$scheduler_tables_access.count, 0, actual);
    osp$clear_mainframe_sig_lock (jmv$scheduler_tables_access.lock);

{ If the job is a leveled job it must fit the categories of one of the
{ mainframes in the scheduling profile.  If the job is NOT a leveled job
{ then its categories must match the mainframe on which the job is being
{ submitted.

{ NOTE: The first set of categories in the scheduler table represent the categories
{ of the executing mainframe.

    valid_mainframes_set := $jmt$valid_mainframe_set [1];
    fits_a_mainframe := FALSE;
    IF categories_fit (job_category_set, jmv$job_scheduler_table.validation_categories_p^ [1].required,
          jmv$job_scheduler_table.validation_categories_p^ [1].excluded) THEN
      fits_a_mainframe := TRUE;
    IFEND;
    IF leveled_job THEN
      login_family_index := 0;

    /search_for_family_index/
      FOR family_table_index := 1 TO UPPERBOUND (osv$family_table^) DO
        IF osv$family_table^ [family_table_index].family_name = login_family THEN
          login_family_index := family_table_index;
          EXIT /search_for_family_index/;
        IFEND;
      FOREND /search_for_family_index/;

      IF login_family_index = 0 THEN
        osp$system_error ('A family on the server is missing for categorization.', NIL);
      ELSE

      /fits_mainframe/
        FOR mainframe_index := 2 TO UPPERBOUND (jmv$job_scheduler_table.validation_categories_p^) DO
          family_mainframe_list_p := osv$family_table^ [login_family_index].p_client_access_list;
          family_access := osv$family_table^ [login_family_index].default_family_access;

        /search_for_mainframe/
          WHILE family_mainframe_list_p <> NIL DO
            IF family_mainframe_list_p^.client_binary_id = jmv$job_scheduler_table.
                  validation_categories_p^ [mainframe_index].binary_mainframe_id THEN
              family_access := family_mainframe_list_p^.family_access;
              EXIT /search_for_mainframe/;
            ELSE
              family_mainframe_list_p := family_mainframe_list_p^.p_next_client;
            IFEND;
          WHILEND /search_for_mainframe/;

          IF (dfc$job_leveling_access IN family_access) THEN
            valid_mainframes_set := valid_mainframes_set + $jmt$valid_mainframe_set [mainframe_index];
            fits_a_mainframe := fits_a_mainframe OR categories_fit
                  (job_category_set, jmv$job_scheduler_table.validation_categories_p^ [mainframe_index].
                  required, jmv$job_scheduler_table.validation_categories_p^ [mainframe_index].excluded);
          IFEND;
        FOREND /fits_mainframe/;
      IFEND;
    IFEND;

    osp$decrement_locked_variable (jmv$scheduler_tables_access.count, actual, actual, lock_error);

    IF NOT fits_a_mainframe THEN
      osp$set_status_condition (jme$no_mainframe_found_for_job, status);
    IFEND;
  PROCEND qfp$determine_mainframe_fitness;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] qfp$get_profile_mainframe_index', EJECT ??
*copy qfh$get_profile_mainframe_index

  PROCEDURE [XDCL] qfp$get_profile_mainframe_index
    (    binary_mainframe_id: pmt$binary_mainframe_id;
     VAR mainframe_index: jmt$maximum_mainframes);

    VAR
      profile_mainframe_index: jmt$maximum_mainframes;

    mainframe_index := 0;

  /search_for_mainframe/
    FOR profile_mainframe_index := 1 TO UPPERBOUND (jmv$job_scheduler_table.validation_categories_p^) DO
      IF binary_mainframe_id = jmv$job_scheduler_table.validation_categories_p^ [profile_mainframe_index].
            binary_mainframe_id THEN
        mainframe_index := profile_mainframe_index;
        EXIT /search_for_mainframe/;
      IFEND;
    FOREND /search_for_mainframe/;

  PROCEND qfp$get_profile_mainframe_index;
?? OLDTITLE ??
MODEND qfm$job_categorization_manager;
