From 9de5fc6b9c2443cbf4f631687fade58fa7811750 Mon Sep 17 00:00:00 2001 From: moshix Date: Thu, 19 Jun 2025 20:54:03 +0200 Subject: [PATCH] sabre.rex v0.2 --- sabre.rexx | 503 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 406 insertions(+), 97 deletions(-) diff --git a/sabre.rexx b/sabre.rexx index d5a075a..067737f 100644 --- a/sabre.rexx +++ b/sabre.rexx @@ -1,6 +1,8 @@ /* SABRE CRS DEMO IN BREXX FOR VM/370 CMS BY ERNIETECH - ENHANCED VERSION */ /* EXPANDED WITH DYNAMIC DATA AND MORE SABRE COMMANDS - - JUNE2025 MOSHIX - AUSTRIA*/ + - JUNE2025 MOSHIX - AUSTRIA + - V0.2 with new commands and more realistic seat maps + */ /* Clear screen in a cross-platform way */ address command @@ -122,6 +124,27 @@ eqDesc.B767 = 'Boeing 767-300' eqDesc.B757 = 'Boeing 757-200' eqDesc.MD981 = 'MD-8-81' +/* Seat map configurations */ +/* Format: ROWS|LAYOUT|EXIT ROWS|FIRST CLASS ROWS|WHEELCHAIR ROWS|BASSINET ROWS|BLOCKED SEATS */ +seatConfig.A320 = '27|ABC DEF|11 12|1-4|1 27|12|14B 14E' /* 3-3 config, 158 seats */ +seatConfig.B738 = '28|ABC DEF|14 15|1-4|1 28|15|16B 16E' /* 3-3 config, 162 seats */ +seatConfig.B789 = '42|ABC DEF GHJ|24 25|1-5|1 42|25|26E 26F' /* 3-3-3 config, 290 seats */ +seatConfig.A350 = '44|ABC DEF GHJ|24 25|1-5|1 44|25|26E 26F' /* 3-3-3 config, 308 seats */ +seatConfig.B77W = '51|ABC DEFG HJK|24 25|1-4|1 51|25|26E 26F' /* 3-4-3 config, 357 seats */ +seatConfig.B747 = '58|ABC DEFG HJK|24 25|1-4|1 58|25|26E 26F' /* 3-4-3 config, 412 seats */ +seatConfig.B767 = '34|ABC DEF GH|20 21|1-3|1 34|21|22D 22E' /* 2-3-2 config, 203 seats */ +seatConfig.B757 = '25|ABC DEF|14 15|1-3|1 25|15|16B 16E' /* 3-3 config, 152 seats */ +seatConfig.MD981 = '23|ABC DE|10 11|1-2|1 23|11|12B 12D' /* 2-3 config, 109 seats */ + +/* Seat map legend */ +seatLegend.A = 'Available' +seatLegend.X = 'Occupied' +seatLegend.B = 'Blocked' +seatLegend.F = 'First Class' +seatLegend.E = 'Exit Row' +seatLegend.W = 'Wheelchair' +seatLegend.C = 'Bassinet' +seatLegend.M = 'Blocked Middle' /* Seat configurations per equipment type */ eqSeats.A320 = 158 @@ -202,11 +225,13 @@ say /* Initialize flight seat tracking */ drop flightSeats. flightSeats.0 = 0 +currentPNR = '' /* Track current PNR */ do forever say pull cmd cmd = strip(translate(cmd)) + if cmd = '' then iterate /* Skip empty commands */ /* HELP & SIGN-OUT */ if cmd = 'HELP' then do @@ -224,8 +249,13 @@ do forever say "WP/NI Display alternate fare options" say "N/ADD- Add passenger name (N/ADD-1 DOE/JOHN ADT)" say "N/CHG- Change passenger name (N/CHG-1 DOE/JANE)" + say "4G* Display seat map for segment n" say "SO* Sign Out all Work Areas" - iterate + say + say "Enter command:" + pull cmd + if cmd = '' then iterate + cmd = strip(translate(cmd)) end if cmd = 'SO*' then do @@ -236,6 +266,330 @@ do forever /* split into verb + rest */ parse var cmd first rest + /* --- Seat Map Display and Assignment (4G command) --- */ + if left(cmd,2) = '4G' then do + /* Check if this is a seat assignment command */ + if pos('S', substr(cmd,3)) > 0 then do + /* Parse the command - handle both formats */ + if pos('PNR', substr(cmd,3)) > 0 then do + /* Format: 4GPNR001S2A */ + parse var cmd '4GPNR' pnrRaw 'S' seatAssign + if left(pnrRaw,3) \= 'PNR' then do + /* Extract numeric part before S */ + pnrNum = '' + do i = 1 to length(pnrRaw) until substr(pnrRaw,i,1) = 'S' + if datatype(substr(pnrRaw,i,1),'W') then + pnrNum = pnrNum||substr(pnrRaw,i,1) + end + if pnrNum = '' then do + say '*** Invalid PNR number' + iterate + end + padded = substr('000'||pnrNum,length('000'||pnrNum)-2,3) + currentPNR = 'PNR'||padded + end + else currentPNR = pnrRaw + segNum = 1 /* Default to segment 1 */ + end + else do + /* Format: 4G1S2A */ + parse var cmd '4G' segNum 'S' seatAssign + if \datatype(segNum,'W') then do + say '*** Invalid segment number' + iterate + end + end + + /* Handle numeric conversion safely */ + segNum = strip(segNum) + 0 /* Force numeric */ + + /* Parse seat assignment (e.g., 2A) */ + seatAssign = strip(translate(seatAssign)) + /* Split into row and seat letter */ + do i = 1 to length(seatAssign) + if \datatype(substr(seatAssign,i,1),'W') then leave + end + if i = 1 | i > length(seatAssign) then do + say '*** Invalid seat format - must be ROW+LETTER (e.g., 2A)' + iterate + end + seatRow = substr(seatAssign,1,i-1) + 0 /* Force numeric */ + seatLetter = substr(seatAssign,i) + + /* Find the PNR and flight */ + found = 0 + do j = 1 to PNRdata.0 + parse var PNRdata.j pnrEntry pname oldSeat flightNum + if pnrEntry = currentPNR then do + parse var matchList.flightNum airline flight depc depd dept arrt arrc avst eqmt + flightKey = airline||flight||depc||arrc||depd||dept + found = 1 + leave + end + end + + if \found then do + say '*** PNR' currentPNR 'NOT FOUND' + iterate + end + + /* Get seat configuration */ + parse var seatConfig.eqmt rows '|' layout '|' exitRows '|' firstRows '|' wheelRows '|' bassRows '|' blockSeats + + /* Validate seat exists in configuration */ + if seatRow < 1 | seatRow > (rows + 0) then do + say '*** Invalid row number' seatRow 'for' eqDesc.eqmt + iterate + end + + /* Validate seat letter */ + if pos(seatLetter, layout) = 0 then do + say '*** Invalid seat letter' seatLetter 'for' eqDesc.eqmt + iterate + end + + /* Check if seat is available */ + newSeat = seatRow||seatLetter + if symbol('seatMap.'flightKey'.'newSeat) \= 'LIT' then do + if seatMap.flightKey.newSeat = 'X' then do + say '*** Seat' newSeat 'is not available' + iterate + end + end + + /* Check if seat is blocked */ + if wordpos(newSeat, blockSeats) > 0 then do + say '*** Seat' newSeat 'is blocked' + iterate + end + + /* Release old seat if exists */ + if oldSeat \= '' then do + seatMap.flightKey.oldSeat = '' + end + + /* Assign new seat */ + seatMap.flightKey.newSeat = 'X' + + /* Update PNR record */ + PNRdata.j = pnrEntry pname newSeat flightNum + + say 'Seat changed to' newSeat 'for' currentPNR + iterate + end + + /* Handle regular seat map display */ + if pos('PNR', substr(cmd,3)) > 0 then do + /* Format: 4GPNR001 */ + pnrRaw = substr(cmd,5) /* Skip '4G' and 'PNR' */ + if left(pnrRaw,3) \= 'PNR' then do + padded = substr('000'||pnrRaw,length('000'||pnrRaw)-2,3) + pnr = 'PNR'||padded + end + else pnr = pnrRaw + currentPNR = pnr + segNum = 1 /* Default to segment 1 */ + end + else do + /* Format: 4G1* */ + parse var cmd '4G' segNum '*' + if segNum = '' then segNum = 1 + if \datatype(segNum,'W') then do + say '*** Invalid segment number' + say + say "Enter command:" + pull cmd + if cmd = '' then iterate + cmd = strip(translate(cmd)) + parse var cmd first rest + end + end + + /* Handle numeric conversion safely */ + segNum = strip(segNum) /* Remove any whitespace */ + if \datatype(segNum,'W') then segNum = 1 + segNum = segNum + 0 /* Force numeric */ + + /* Check if we have an active PNR */ + if currentPNR = '' then do + say '*** NO ACTIVE PNR - DISPLAY PNR FIRST' + say + say "Enter command:" + pull cmd + if cmd = '' then iterate + cmd = strip(translate(cmd)) + parse var cmd first rest + end + + /* Find the flight */ + found = 0 + do j = 1 to PNRdata.0 + parse var PNRdata.j pnrEntry pname seat flightNum + if pnrEntry = currentPNR then do + parse var matchList.flightNum airline flight depc depd dept arrt arrc avst eqmt + flightKey = airline||flight||depc||arrc||depd||dept + found = 1 + leave + end + end + + if \found then do + say '*** PNR' currentPNR 'NOT FOUND' + say + say "Enter command:" + pull cmd + if cmd = '' then iterate + cmd = strip(translate(cmd)) + parse var cmd first rest + end + + /* Get seat configuration */ + parse var seatConfig.eqmt rows '|' layout '|' exitRows '|' firstRows '|' wheelRows '|' bassRows '|' blockSeats + if rows = '' then do + say '*** Invalid equipment type:' eqmt + say + say "Enter command:" + pull cmd + if cmd = '' then iterate + cmd = strip(translate(cmd)) + parse var cmd first rest + end + + /* Force numeric conversion */ + rows = rows + 0 + + /* Initialize seat map if not already done */ + if symbol('seatMapInit.'flightKey) = 'LIT' then do + /* Calculate number of seats to mark as occupied */ + totalSeats = eqSeats.eqmt + 0 /* Force numeric */ + availSeats = avst + 0 /* Force numeric */ + seatsToOccupy = totalSeats - availSeats + + if seatsToOccupy > 0 then do + /* Get list of all possible seats */ + allSeats = '' + do r = 1 to rows + do c = 1 to length(layout) + ch = substr(layout,c,1) + if ch = ' ' then iterate + if wordpos(r||ch, blockSeats) > 0 then iterate + allSeats = allSeats r||ch' ' + end + end + + /* Randomly mark seats as occupied */ + do while seatsToOccupy > 0 & words(allSeats) > 0 + /* Pick a random seat */ + idx = (time('S') // words(allSeats)) + 1 + seat = word(allSeats,idx) + /* Remove it from available seats */ + allSeats = delword(allSeats,idx,1) + /* Mark it as occupied */ + seatMap.flightKey.seat = 'X' + seatsToOccupy = seatsToOccupy - 1 + end + end + + /* Mark initialization as done */ + seatMapInit.flightKey = 1 + end + + /* Display header */ + say 'SEAT MAP FOR' airline||flight eqDesc.eqmt + say 'DATE:' depd 'SEGMENT:' segNum + say + + /* Display column headers based on layout */ + colHeader = '' + do i = 1 to length(layout) + if substr(layout,i,1) = ' ' then + colHeader = colHeader ' ' + else + colHeader = colHeader substr(layout,i,1) + end + say ' 'colHeader + + /* Display seat map */ + do r = 1 to rows + rowNum = right(r,2) + rowStr = rowNum' ' + + do c = 1 to length(layout) + ch = substr(layout,c,1) + if ch = ' ' then do + rowStr = rowStr' ' + iterate + end + + seatCode = r||ch + /* Check seat status */ + status = 'A' /* Default to Available */ + + /* Check if seat is occupied */ + if symbol('seatMap.'flightKey'.'seatCode) \= 'LIT' then + status = seatMap.flightKey.seatCode + + /* Check if blocked middle seat */ + if wordpos(seatCode, blockSeats) > 0 then + if status = 'A' then status = 'M' + + /* Check if wheelchair accessible */ + if wordpos(r, wheelRows) > 0 then + if status = 'A' then status = 'W' + + /* Check if bassinet position */ + if wordpos(r, bassRows) > 0 then + if status = 'A' then status = 'C' + + /* Check if exit row */ + if wordpos(r, exitRows) > 0 then + if status = 'A' then status = 'E' + + /* Check if first class */ + if wordpos(r, firstRows) > 0 then + if status = 'A' then status = 'F' + + rowStr = rowStr||status + end + say rowStr + end + + say + say 'A=AVAILABLE X=OCCUPIED E=EXIT ROW F=FIRST CLASS' + say 'B=BLOCKED W=WHEELCHAIR C=BASSINET M=BLOCKED MIDDLE' + iterate + end + + /* --- PNR lookup command --- */ + if first = 'PNR' then do + parse var rest pnrRaw + pnrNorm = strip(translate(pnrRaw)) + if left(pnrNorm,3) \= 'PNR' then do + padded = substr('000'||pnrNorm,length('000'||pnrNorm)-2,3) + pnr = 'PNR'||padded + end + else pnr = pnrNorm + + found = 0 + do j = 1 to PNRdata.0 + parse var PNRdata.j pnrEntry pname seat flightRec + if pnrEntry = pnr then do + currentPNR = pnr /* Set as current PNR */ + say 'PNR '||pnrEntry||' => '||pname||' SEAT '||seat||' '||flightRec + /* Display passenger type if exists */ + if paxType.j \= '' then + say ' Type: '||paxTypes.paxType.j + /* Display FF info if exists */ + if ff.pnr \= '' then + say ' FF: '||ff.pnr||' ('||ff.pnr.airline||')' + found = 1 + leave + end + end + if found = 0 then say '*** PNR '||pnr||' not found.' + iterate + end + /* --- BOOK command with seat & name prompting --- */ if first = 'BOOK' then do parse var rest num @@ -270,10 +624,34 @@ do forever say "* Enter passenger name as (no spaces):" pull pname - /* assign next seat */ - seatCode = genRandomNum(1, 100) /* Simple seat number */ + /* Get seat configuration */ + parse var seatConfig.eqmt rows '|' layout '|' exitRows '|' firstRows '|' wheelRows '|' bassRows '|' blockSeats - /* store booking */ + /* Find next available seat */ + seatFound = 0 + do r = 1 to rows + 0 until seatFound /* Force numeric */ + do c = 1 to length(layout) until seatFound + if substr(layout,c,1) = ' ' then iterate + seatCode = r||substr(layout,c,1) + + /* Skip if seat is blocked middle */ + if wordpos(seatCode, blockSeats) > 0 then iterate + + /* Check if seat is available */ + if symbol('seatMap.'flightKey'.'seatCode) = 'LIT' then do + seatFound = 1 + seatMap.flightKey.seatCode = 'X' /* Mark as occupied */ + leave + end + end + end + + if \seatFound then do + say "*** No seats available" + iterate + end + + /* store booking with actual seat assignment */ j = pnrnum PNRdata.j = pnr||' '||pname||' '||seatCode||' '||matchList.num @@ -517,35 +895,6 @@ do forever iterate end - /* --- PNR lookup command --- */ - if first = 'PNR' then do - parse var rest pnrRaw - pnrNorm = strip(translate(pnrRaw)) - if left(pnrNorm,3) \= 'PNR' then do - padded = substr('000'||pnrNorm,length('000'||pnrNorm)-2,3) - pnr = 'PNR'||padded - end - else pnr = pnrNorm - - found = 0 - do j = 1 to PNRdata.0 - parse var PNRdata.j pnrEntry pname seat flightRec - if pnrEntry = pnr then do - say 'PNR '||pnrEntry||' => '||pname||' SEAT '||seat||' '||flightRec - /* Display passenger type if exists */ - if paxType.j \= '' then - say ' Type: '||paxTypes.paxType.j - /* Display FF info if exists */ - if ff.pnr \= '' then - say ' FF: '||ff.pnr||' ('||ff.pnr.airline||')' - found = 1 - leave - end - end - if found = 0 then say '*** PNR '||pnr||' not found.' - iterate - end - /* --- Equipment Query Commands --- */ if left(cmd,4) = 'W/EQ' then do if length(cmd) > 5 & substr(cmd,5,1) = '*' then @@ -625,65 +974,9 @@ do forever drop flightSeats. flightSeats.0 = 0 - say - say date||' '||from||'/'||to||' ----------------------' - say ' # ARLN FLTN DEPC/ARVC DEPT ARRT AVST EQTYP' - say '-----------------------------------------------------------------------' - - matchList.0 = 0 - if flights.0 = 0 then do - say "NO FLIGHTS AVAILABLE FOR THAT QUERY" - iterate - end - - do i = 1 to flights.0 - parse var flights.i airline flight depc depd dept arrt arrc avst eqmt - - /* time-of-day filter if specified */ - match = 1 - if time \= '' then do - parse var dept hr 3 mn 5 ap 6 - if \datatype(hr,'W') | \datatype(mn,'W') then iterate - mins = (hr * 60) + mn - if ap = 'P' & hr \= 12 then mins = mins + (12 * 60) - if ap = 'A' & hr = 12 then mins = 0 - - timeHr = substr(time,1,length(time)-1) - timeAP = right(time,1) - if \datatype(timeHr,'W') then iterate - targetMins = (timeHr * 60) - if timeAP = 'P' & timeHr \= 12 then targetMins = targetMins + (12 * 60) - if timeAP = 'A' & timeHr = 12 then targetMins = 0 - - /* Allow 2 hour window around requested time */ - if abs(mins - targetMins) > 120 then match = 0 - end - - if match then do - idx = matchList.0 + 1 - matchList.0 = idx - matchList.idx = airline flight depc depd dept arrt arrc avst eqmt - - /* Initialize seat tracking with numeric value */ - flightKey = airline||flight||depc||arrc||depd||dept - numeric_avst = avst + 0 /* Force numeric conversion */ - flightSeats.flightKey = numeric_avst - - out = right(idx,2) - out = out||' '||left(airline,5) - out = out||' '||left(flight,7) - out = out||' '||left(depc||'/'||arrc,11) - out = out||' '||left(dept,7) - out = out||' '||left(arrt,7) - out = out||' '||left(avst,6) - out = out||' '||eqmt - say out - end - end - - if matchList.0 = 0 then - say "NO FLIGHTS AVAILABLE FOR THAT QUERY" -end + /* Display flight list */ + call displayFlights date, from, to +end /* End of main loop */ /* Function definitions */ genRandomNum: @@ -891,6 +1184,15 @@ displayFlights: say ' # ARLN FLTN DEPC/ARVC DEPT ARRT AVST EQTYP' say '-----------------------------------------------------------------------' + /* Initialize matchList stem */ + drop matchList. + matchList.0 = 0 + 0 /* Force numeric */ + + if flights.0 = 0 then do + say "NO FLIGHTS AVAILABLE FOR THAT QUERY" + return + end + do i = 1 to flights.0 parse var flights.i airline flight depc depd dept arrt arrc avst eqmt @@ -915,11 +1217,18 @@ displayFlights: end if match then do - idx = matchList.0 + 1 - matchList.0 = idx - /* Store numeric seat count in a separate field */ + /* Safe numeric conversion for index */ + idx = matchList.0 + 0 /* Force current value to numeric */ + idx = idx + 1 /* Increment */ + matchList.0 = idx /* Store back */ + + /* Store flight info */ matchList.idx = airline flight depc depd dept arrt arrc avst eqmt - matchList.idx.seats = avst /* Store seats as a separate numeric field */ + + /* Initialize seat tracking with numeric value */ + flightKey = airline||flight||depc||arrc||depd||dept + numeric_avst = avst + 0 /* Force numeric conversion */ + flightSeats.flightKey = numeric_avst out = right(idx,2) out = out||' '||left(airline,5)