#Setup - install Minimacy, as described in the [Setup section](page://2000.100.100). Build Minimacy or use the Release distribution. - clone the demo repository: >git clone https://github.com/ambermind/airport-display.git We assume now that: - [minimacy] is the full path of the Minimacy directory, it contains at least these subfolders: ./bin ./rom ./programs - [minimacyBinary] is the full path of Minimacy binary with UI (minimacy.exe on Windows, minimacyX11GL on Unix, minimacyMacX11GL on MacOS). - [airport-display] is the full path of this example on your disk Then, you may start this example with: >[minimacyBinary] [airport-display]/boot.mcy Alternately, you may replace the content of [minimacy]/programs with the content of [airport-display]. It will overwrite 'boot.mcy'. You should create a dedicated copy of Minimacy in order to do so. Then, you have nothing more to do to build iOS, Android or MacOS app. See [Building an App (MacOS, iOS, Android)](page://2000.300.100) #Usage Once started, a window should open with this content: [](html) This demonstration retrieves the schedule of next flights at CDG (Charles-de-Gaulle airport near Paris, France) from an internet website, then: - display the schedule, page after page - refresh the list of next flights every minute #Implementation The considered airport is defined by its 3-letters code. You may change it as you wish. const AIRPORT="cdg";; This demo gets the schedule from: [https://www.avionio.com/widget/en/cdg/departures](https://www.avionio.com/widget/en/cdg/departures) This url is a html which includes a timestamp. In order to get more than 1 page, you need to add ts and page parameters to the url (?ts=*&page=*). So the logic is the following: - get the first page - extract the timestamp - loop on the next pages providing the timestamp and the page number starting from 1. We implement a function 'avionioParse' which get the url with [[httpGet]], then parse the html code with [[htmlParse]], then extract the list of flights. Each flight is a tuple [hour, destination, flight, company, estimated]. The function also extracts the timestamp and the title and returns a tuple [title, ts, listItems] where ts is the timestamp and listItems is the list of flights tuples. The parsing greatly depends on the html page structure. It must be adapted when the origin website has changed. fun avionioParse(suffix)= let echoLn strConcat(URL, suffix) -> url in let httpGet(url) -> data in let htmlParse(data, 0, false) -> [xml, _] in let head(xmlFilterChilds(xml, "body")) -> xml in let head(xmlFilterChilds(xml, "main")) -> xml in let strTrim(xmlGetContent(head(xmlFilterChilds(xml, "h1")))) -> title in let head(xmlFilterChilds(xml, "table")) -> xml in let head(xmlFilterChilds(xml, "tbody")) -> xml in let xmlFilterChilds(xml, "tr") -> listItems in let head(xmlFilterChilds(head(listItems), "td")) -> xmlNav in let head(xmlFilterChilds(xmlNav, "a")) -> xmlNav in let xmlGetAttribute(xmlNav, "href") -> href in let head(strSplit("&", head(tail(strSplit("?ts=", href))))) -> ts in let listFilter(listItems, lambda(xml) = strContains(xmlGetAttribute(xml, "class"), "tt-row")) -> listItems in let listMap(listItems, lambda(xml) = let xmlFilterChilds(xml, "td") -> columns in let listMap(columns, lambda(xml) = strTrim(xmlGetContent(xml))) -> columns in let columns -> hour::_::_::destination::flight::company::estimated::_ in [hour, destination, flight, company, estimated] ) -> listItems in ( echoLn strFormat("nbItems= *", listLength(listItems)); [title, ts, listItems] );; This function use the [[httpGet]] function which is synchronous: the function halts until the reply is received. Then we can implement 'avionioLoad' which get the firstpage and then loops to get a total of NB_SERVER_PAGES pages. Items are concatenated and stored in the global variable FlightList. The title is also stored in the global variable BoardTitle. fun avionioLoad ()= let avionioParse(nil) -> [title, ts, listItems] in ( for i=1;i [_, _, listItemsNext] in set listItems=listConcat(listItems, listItemsNext); if listItems<>nil then ( set FlightList=listItems; set BoardTitle=title ); onTimeout(1000*REFRESH_DATA_PERIOD, #avionioLoad) );; Finaly we set a timer of REFRESH_DATA_PERIOD seconds. The the timer fires, it calls avionioLoad again, and therefore refresh the list of flights. The 'run' function calls 'avionLoad' to get a first list of flights. It starts the UI with the BoardTitle, then calls 'flightDraw' to display de flights. fun main ()= avionioLoad(); uiStart(0, 0, WIDTH, HEIGHT, UI_RESIZE, BoardTitle); flightDraw(0);; We first need a Font to draw the text. const FontText= fontFromBitmap(bitmapFromPng(load("rsc/fonts/Arial_32_256.png")));; We load such a font from a bitmap you will found in ./minimacy/rom/rsc/fonts/Arial_32_256.png Fonction composition could be read like this: - first we [[load]] the file - we assume it is a png file and call [[bitmapFromPng]] to create a bitmap from it - finally we call [[fontFromBitmap]] to make a font from it. More information about these bitmaps in [core.2d.font](page://ref.core.2d.font) and more specificaly in [[fontFromBitmap]]. You may create your own font bitmaps using [FontMaker](https://minimacy.net/book/#/font_maker). Then, we define a recursive function to draw one flight at a specific vertical coordinate 'y' and recursively draw the next flights from 'y+ROW_HEIGHT'. fun _flightDrawItem(listItems, y)= if listItems<>nil && y+ROW_HEIGHT <= HEIGHT-MARGIN_Y then let head(listItems) -> [hour, destination, flight, company, estimated] in ( bitmapTextU8(uiBuffer(), MARGIN_X, y, 0, hour, FontText, COLOR_TEXT, nil); bitmapTextU8(uiBuffer(), MARGIN_X+80, y, 0, destination, FontText, COLOR_TEXT, nil); bitmapTextU8(uiBuffer(), WIDTH/2-140, y, 0, flight, FontText, COLOR_TEXT, nil); bitmapTextU8(uiBuffer(), WIDTH/2, y, 0, company, FontText, COLOR_TEXT, nil); bitmapTextU8(uiBuffer(), WIDTH-MARGIN_X, y, ALIGN_RIGHT, estimated, FontText, COLOR_TEXT, nil); bitmapLine(uiBuffer(), 0, y+ROW_HEIGHT-5, WIDTH, y+ROW_HEIGHT-5, COLOR_SEPARATOR, nil); _flightDrawItem(tail(listItems), y+ROW_HEIGHT) );; We stop the recursion when listItems is empty or when the 'y' coordinate is outside the screen minus a margin MARGIN_Y. Else we extract the flight tuple and use [[bitmapTextU8]] to draw texts. We also draw an horizontal line with [[bitmapLine]] to separate each line. Colors are provided as 32 bits ARGB integers. The last function draws a page of flights and set a timer to draw the next page. The function is called with a parameter 'skip' which indicates the index of the first flight to display, therefore the page to display. It is first called with skip=0. We first compute the number of flights per page in 'nbItemsPerPage'. Then we "update" the skip value, so that if it is bigger than the number of flights, it resets to zero and displays the first page again. fun flightDraw(skip)= let FlightList -> listItems in let listLength(listItems) -> nbItems in let (HEIGHT-MARGIN_Y*2)/ROW_HEIGHT -> nbItemsPerPage in let if skip skip in ( echoLn strFormat("display flights */*", skip, nbItems); bitmapErase(uiBuffer(), COLOR_BACKGROUND); _flightDrawItem(listSkip(listItems, skip), MARGIN_Y); bitmapTextU8(uiBuffer(), WIDTH-MARGIN_X, HEIGHT-2, ALIGN_RIGHT|ALIGN_BOTTOM, BoardTitle, FontText, COLOR_TITLE, nil); uiUpdate(); onTimeout(1000*NEXT_PAGE_PERIOD, lambda ()= flightDraw(skip+nbItemsPerPage)) );; Then the drawing itself: - [[bitmapErase]] on [[uiBuffer]] to reset the screen buffer with the specified color. - '_flightDrawItem' to display the page of flights, starting at y=MARGIN_Y - [[bitmapTextU8]] to display the title in the bottom right - [[uiUpdate]] to send the screen buffer [[uiBuffer]] to the screen Finally we call [[onTimeout]] to call itself ('flightDraw') after NEXT_PAGE_PERIOD seconds, with the next flights (skipping skip+nbItemsPerPage).