Korzystanie z języka skryptów CSCS do rozwoju międzyplatformowego
Opublikowany: 2022-03-10Naszym celem nie jest zbudowanie platformy; to ma być skrzyżowane z nimi wszystkimi.
— Mark Zuckerberg
CSCS (Customized Scripting in C#) to język skryptowy o otwartym kodzie źródłowym zaimplementowany w C#. Pod względem składniowym jest bardzo podobny do JavaScriptu, ale ma też pewne podobieństwa do Pythona. Niektóre z tych podobieństw są słowami kluczowymi w dobrze znanej konstrukcji if…elif…else
, a także mają taką samą definicję zakresu zmiennej jak w Pythonie (np. zmienna zdefiniowana wewnątrz bloku if
lub wewnątrz pętli będzie również widoczna na zewnątrz) .
W przeciwieństwie do JavaScript i Pythona w zmiennych i funkcjach CSCS nie jest rozróżniana wielkość liter. Podstawowym celem CSCS jest umożliwienie programiście pisania jak najmniejszej ilości kodu . Ponadto ten sam kod jest używany zarówno do programowania na iOS, jak i na Androida. Dodatkowo CSCS może być używany do tworzenia systemów Windows, Mac i Unity.
Uwaga : Możesz przeczytać więcej o tym, jak firma Microsoft używa CSCS w swoim produkcie Maquette (opartym na Unity) tutaj.
CSCS można dodać do projektu, osadzając jego kod źródłowy C# w projekcie Visual Studio Xamarin. W przeciwieństwie do większości innych języków, masz pełną własność kodu źródłowego CSCS i możesz łatwo dodawać lub modyfikować jego funkcjonalność. Podam tego przykład w dalszej części artykułu.
Ponadto dowiemy się, jak rozpocząć korzystanie z CSCS i korzystać z bardziej zaawansowanych funkcji, które zostały omówione w innych artykułach. Wśród tych funkcji będziemy uzyskiwać dostęp do usługi sieciowej za pośrednictwem żądań sieci Web z analizą ciągów JSON, a także będziemy używać SQLite na iOS i Androida.
Najłatwiejszym sposobem rozpoczęcia jest pobranie próbki projektu za pomocą CSCS i rozpoczęcie zabawy z plikiem start.cscs . Tym zajmiemy się w następnej sekcji: tworzenie aplikacji na iOS/Android z podstawowym GUI i zdarzeniami.
"Witaj świecie!" W CSCS
Zacznijmy od stosunkowo prostego przykładu kodu CSCS, który konstruuje ekran z kilkoma widżetami:
AutoScale(); SetBackgroundColor("light_green"); locLabelText = GetLocation("ROOT", "CENTER", "ROOT", "TOP"); AddLabel(locLabelText, "labelText", "Welcome " + _DEVICE_INFO_ + " " + _VERSION_INFO_ + " User!", 600, 100); locTextEdit = GetLocation("ROOT", "LEFT", labelText, "BOTTOM"); AddTextEdit(locTextEdit, "textEdit", "Your name", 320, 80); locButton = GetLocation(textEdit,"RIGHT",textEdit, "CENTER"); AddButton(locButton, "buttonHi", "Hello", 160, 80); function buttonHi_click(sender, arg) { name = getText(textEdit); msg = name != "" ? "Hello, "+ name + "!" : "Hello, World!"; AlertDialog("My Great App", msg); }
Poniższy obrazek pokazuje wynikowy interfejs użytkownika na iPhonie, a także urządzeniu z Androidem po kliknięciu przycisku „Hello” i nie wpisaniu niczego w polu „Edycja tekstu”:
Przyjrzyjmy się pokrótce powyższemu kodowi. Rozpoczyna się wywołaniem funkcji AutoScale()
, która informuje parser, że rozmiary widżetów są zależne od rozmiaru ekranu, tzn. zostaną automatycznie zmienione (widżet będzie wyglądał na większy na większych ekranach i mniejszy na mniejszych ekrany). To ustawienie może być również nadpisane dla każdego widżetu.
Zauważ, że nie ma potrzeby tworzenia specjalnego uchwytu na kliknięcie przycisku. Jeśli zdefiniujesz funkcję o nazwie widgetName_click()
, zostanie ona użyta jako procedura obsługi, gdy użytkownik kliknie widget o nazwie widgetName
(nie musi to być przycisk, w rzeczywistości może to być dowolny widget). Dlatego funkcja buttonHi_click()
zostanie uruchomiona, gdy tylko użytkownik kliknie przycisk.
Być może zauważyłeś, że GUI jest zbudowany całkowicie w kodzie. Odbywa się to poprzez podanie względnej lokalizacji widżetu podczas dodawania go. Ogólny format polecenia lokalizacji jest następujący:
location = GetLocation(WidgetX, HorizontalPlacement, WidgetY, VerticalPlacement, deltaX=0, deltaY=0, autoResize=true);
Możesz więc umieścić widżet w stosunku do innych widżetów na ekranie. Szczególnym przypadkiem widżetu jest widżet „ROOT”, czyli ekran główny.
Po utworzeniu lokalizacji musisz podać ją jako argument do dowolnej z następujących funkcji:
-
AddLabel
, -
AddButton
, -
AddCombobox
, -
AddStepper
, -
AddListView
, -
AddTextView
, -
AddStepper
, -
AddImageView
, -
AddSlider
, -
AddPickerView
, - i tak dalej.
Wszystkie powyższe mają tę samą strukturę:
AddButton(location, newWidgetname, initialValue, width, height);
Szerokość i wysokość widżetu będą zależne od rozmiaru ekranu, jeśli wcześniej uruchomiono polecenie AutoScale()
CSCS. Również wartością początkową (w przypadku przycisku) jest wyświetlany na nim tekst. Można to zmienić w dowolnym momencie, wywołując SetText(widgetName, newText)
.
Używanie kodu Visual Studio do debugowania CSCS
Możemy również użyć Visual Studio Code do debugowania skryptów CSCS. Jeśli chcesz tworzyć aplikacje na Androida i iOS, musisz użyć komputera Mac. Po zainstalowaniu kodu programu Visual Studio zainstaluj rozszerzenie CSCS Debugger i REPL.
Aby użyć rozszerzenia, dodaj ten wiersz kodu w dowolnym miejscu w skrypcie CSCS start.cscs
:
StartDebugger();
Poniższy obrazek pokazuje, jak używać programu Visual Studio Code do debugowania i zmiany funkcji „Witaj świecie!” aplikację, którą opracowaliśmy w poprzedniej sekcji. W nadchodzącym przykładzie dodamy etykietę i przycisk w locie do istniejącego układu.
Aby to zrobić, po prostu wybieramy kod, który ma wykonać parser i wciskamy Ctrl + 8 . W rezultacie na środku ekranu zostanie dodana etykieta i przycisk. Dodajemy również funkcję obsługi przycisku, która zaktualizuje nową etykietę o bieżący czas przy każdym kliknięciu przycisku.
Korzystanie z SQLite w CSCS
SQLite to typ relacyjnej bazy danych typu ACID (Atomicity, Consistency, Isolation, Durability) opracowany przez Richarda Hippa (pierwsza wersja została wydana w 2000 roku). W przeciwieństwie do innych relacyjnych baz danych, takich jak Microsoft SQL Server czy Oracle Database, jest on wbudowany. (Osadzone nie tylko w urządzeniu, ale także w programie końcowym.) Jest dołączona do programu jako bardzo kompaktowa biblioteka o rozmiarze mniejszym niż 500 KB. Ale dwie aplikacje (wydane przez tego samego programistę) mogą czytać tę samą bazę danych SQLite, jeśli ścieżka do pliku DB jest znana obu aplikacjom.
Zaletą SQLite jest to, że można go używać bez dodatkowej instalacji na urządzeniu z systemem iOS lub Android. Wadą jest to, że oczywiście nie może pomieścić tylu danych, co „normalna” baza danych, a ponadto jest słabo napisany (tzn. zamiast liczby całkowitej można wstawić łańcuch — zostanie on następnie przekonwertowany na liczbę całkowitą lub 0 w przypadku niepowodzenia). Z drugiej strony to ostatnie może być również postrzegane jako zaleta.
SQLite może być łatwo używany z CSCS bez dodatkowych instrukcji importu. Oto tabela, która pomoże Ci uzyskać przegląd głównych funkcji SQLite używanych w CSCS:
Komenda | Opis |
---|---|
SQLInit(DBName) | Inicjuje bazę danych lub ustawia bazę danych, która ma być używana z kolejnymi instrukcjami DB. |
SQLDBExists(DBName) | Sprawdza, czy DB została zainicjowana. Ustawia również bazę danych, która ma być używana z kolejnymi instrukcjami DB. |
SQLQuery(query) | Wykonuje zapytanie SQL (instrukcję SELECT). Zwraca tabelę z rekordami. |
SQLNonQuery(nonQuery) | Wykonuje zapytanie SQL, np. update, create lub delete. Zwraca liczbę rekordów, których to dotyczy. |
SQLInsert(tableName, columnList, data) | Wstawia przekazaną tabelę danych rekordów do określonej tabeli DB. Argument columnList ma następującą strukturę: colName1,colName2,…,colNameN |
Tabela 1: Polecenia SQLite w CSCS
Oto jak zwykle używane są funkcje SQLInit()
i SQLDBExists()
:
DBName = "myDB.db1"; if (!SQLDBExists(DBName)) { create = "CREATE TABLE [Data] (Symbol ntext, Low real, High real, Close real, Volume real, Stamp text DEFAULT CURRENT_TIMESTAMP)"; SQLNonQuery(create); } SQLInit(DBName);
Zobaczymy więcej przykładów, jak możesz później wybrać i wstawić dane do bazy danych SQLite. Pokażę ci przykład, jak zapisać dane giełdowe, które zostały wyodrębnione z usługi sieciowej do lokalnej bazy danych SQLite.
Dodawanie niestandardowych funkcji do CSCS
W tej sekcji zobaczymy, jak możesz rozszerzyć funkcjonalność CSCS. Jako przykład zobaczymy poniżej istniejącą implementację funkcji uśpienia CSCS.
Aby dodać niestandardową funkcjonalność, wystarczy utworzyć nową klasę, wywodząc się z klasy ParserFunction
, nadpisując jej metodę Evaluate Evaluate()
i rejestrując tę klasę w parserze. Oto krótka wersja (bez sprawdzania błędów):
class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }
class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }
Rejestracji klasy z parserem można dokonać w dowolnym miejscu na etapie inicjalizacji za pomocą następującego polecenia:
ParserFunction.RegisterFunction("Sleep", new SleepFunction());
Otóż to! Teraz metoda SleepFunction
Evaluate()
klasy SleepFunction będzie wywoływana, gdy tylko token „Sleep” zostanie wyodrębniony przez parser.
Zauważ, że CSCS nie rozróżnia wielkości liter (z wyjątkiem podstawowych instrukcji przepływu sterowania: if
, elif
, else
, for
, while
, function
, include
, new
, class
, return
, try
, throw
, catch
, break
, continue
). Oznacza to, że możesz wpisać „sleep (100)” lub „Sleep (100)” — oba wywołania zawieszą wykonywany wątek na 100 milisekund.
Przetwarzanie JSON w CSSS
JSON (JavaScript Object Notation) to lekki format wymiany danych, składający się z par atrybut-wartość i par typu tablica. Został opracowany przez Douglasa Crockforda na początku 2000 roku (mniej więcej w tym samym czasie, kiedy pojawił się również SQLite).
W tej sekcji dowiemy się, jak parsować JSON za pomocą CSCS.
Funkcja CSCS do analizowania ciągu JSON to GetVariableFromJSON(jsonText)
. Ta funkcja zwraca tabelę skrótów, w której klucze są atrybutami z ciągu JSON.
Rozważmy następujący przykład ciągu JSON:
jsonString = '{ "eins" : 1, "zwei" : "zweiString", "mehr" : { "uno": "dos" }, "arrayValue" : [ "une", "deux" ] }';
Po wywołaniu:
a = GetVariableFromJSON();
Zmienna a
będzie tablicą mieszającą z następującymi wartościami:
a["eins"] = 1 a["zwei"] = "zweiString" a["mehr"]["uno"] = "dos" a["arrayValue"][0] = "une" a["arrayValue"][1] = "deux"
W następnej sekcji zobaczymy inny przykład parsowania ciągu JSON z usługi sieci Web.
Przykład aplikacji z SQLite, żądaniami sieciowymi i JSON
W przypadku aplikacji korzystającej z SQLite, usługi sieciowej i parsowania JSON użyjemy usługi sieciowej Alpha Vantage. Możesz otrzymać klucz API za darmo, ale darmowa wersja umożliwia dostęp do ich usługi internetowej nie więcej niż 5 razy na minutę.
Korzystając z Alpha Vantage, możesz wyodrębnić różne zestawy danych finansowych — w tym ceny akcji. To właśnie zamierzamy zrobić w naszej przykładowej aplikacji.
Poniższy obraz przedstawia wygląd aplikacji Giełda w systemie iOS i na urządzeniu z Androidem.
Kod CSCS do zbudowania GUI jest następujący:
locLabel = GetLocation("ROOT","CENTER", "ROOT","TOP", 0,30); AddLabel(locLabel, "labelRefresh", "", 480, 60); locSFWidget = GetLocation("ROOT","CENTER", labelRefresh,"BOTTOM"); AddSfDataGrid(locSFWidget, "DataGrid", "", graphWidth, graphHeight); listCols = {"Symbol","string", "Low","number", "High", "number", "Close","number", "Volume","number"}; AddWidgetData(DataGrid, listCols, "columns"); colWidth = {17, 19, 19, 19, 26}; AddWidgetData(DataGrid, colWidth, "columnWidth"); locButton = GetLocation("ROOT","CENTER",DataGrid,"BOTTOM"); AddButton(locButton, "buttonRefresh", "Refresh", 160, 80); locLabelError = GetLocation("ROOT","CENTER","ROOT","BOTTOM"); AddLabel(locLabelError, "labelError", "", 600, 160); SetFontColor(labelError, "red"); AlignText(labelError, "center"); getDataFromDB();
Metoda getDataFromDB()
wyodrębni wszystkie dane z bazy danych SQLite. Wykorzystuje zapytanie SQL zdefiniowane w następujący sposób:
query = "SELECT Symbol, Low, High, Close, Volume, DATETIME(Stamp, 'localtime') as Stamp FROM Data ORDER BY Stamp DESC LIMIT 5;";
Spójrz na poniższy kod dotyczący getDataFromDB()
.
function getDataFromDB() { results = SQLQuery(query); for (i = 1; i < results.Size; i++) { vals = results[i]; stock = vals[0]; low = Round(vals[1], 2); high = Round(vals[2], 2); close = Round(vals[3], 2); volume = Round(vals[4], 2); refresh = vals[5]; stockData = {stock, low, high, close, volume}; AddWidgetData(DataGrid, stockData, "item"); } SetText(labelRefresh, "DB Last Refresh: " + refresh); lockGui(false); }
Zobaczmy teraz, jak otrzymujemy dane z usługi internetowej Alpha Vantage. Najpierw inicjujemy dane:
baseURL = "https://www.alphavantage.co/query? " + "function=TIME_SERIES_DAILY&symbol="; apikey = "Y12T0TY5EUS6BC5F"; stocks = {"MSFT", "AAPL", "GOOG", "FB", "AMZN"}; totalStocks = stocks.Size;
Następnie ładujemy zapasy jeden po drugim, gdy tylko użytkownik kliknie przycisk „Odśwież”:
function buttonRefresh_click(object, arg) { lockGui(); SetText(labelRefresh, "Loading ..."); SetText(labelError, ""); ClearWidget(DataGrid); loadedStocks = 0; getData(stocks[loadedStocks]); } function getData(symbol) { stockUrl = baseURL + symbol + "&apikey=" + apikey; WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure"); }
Oto główna funkcja CSCS, której należy użyć w celu uzyskania danych z usługi sieciowej:
WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure");
Ostatnie dwa parametry to funkcje do wywołania po zakończeniu żądania internetowego. Na przykład w przypadku awarii zostanie wywołana następująca funkcja CSCS:
function OnFailure(object, errorCode, text) { SetText(labelError, text); lockGui(false); }
W rezultacie użytkownik otrzyma komunikat o błędzie, jak pokazano poniżej:
Ale jeśli wszystko jest w porządku, przeanalizujemy ciąg JSON i wstawimy jego zawartość do bazy danych SQLite.
function OnSuccess(object, errorCode, text) { jsonFromText = GetVariableFromJSON(text); metaData = jsonFromText[0]; result = jsonFromText[1]; symbol = metaData["2. Symbol"]; lastRefreshed = metaData["3. Last Refreshed"]; allDates = result.keys; dateData = result[allDates[0]]; high = Round(dateData["2. high"], 2); low = Round(dateData["3. low"], 2); close = Round(dateData["4. close"], 2); volume = dateData["5. volume"]; stockData = {symbol, low, high, close, volume}; SQLInsert("Data","Symbol,Low,High,Close,Volume",stockData); if (++loadedStocks >= totalStocks) { getDataFromDB(); } else { getData(stocks[loadedStocks]); } }
Aby zrozumieć, w jaki sposób uzyskujemy dostęp do różnych pól w powyższej tabeli skrótów, spójrzmy na rzeczywisty ciąg otrzymany z żądania internetowego Alpha Vantage:
{ "Meta Data": { "1. Information": "Daily Prices (open, high, low, close) and Volumes", "2. Symbol": "MSFT", "3. Last Refreshed": "2019-10-02 14:23:20", "4. Output Size": "Compact", "5. Time Zone": "US/Eastern" }, "Time Series (Daily)": { "2019-10-02": { "1. open": "136.3400", "2. high": "136.3700", "3. low": "133.5799", "4. close": "134.4100", "5. volume": "11213086" }, … } }
Jak widać, ostatnią datę otrzymujemy jako pierwszy element tablicy allDates
, która składa się ze wszystkich wyodrębnionych dat.
Wniosek
Dodanie CSCS do projektu jest łatwe. Wszystko, co musisz zrobić, to po prostu osadzić kod źródłowy CSCS jako moduł w swoim projekcie — tak jak robi się to w przykładowym projekcie Xamarin.
Używasz i rozszerzasz język skryptowy CSCS w swoich projektach? Zostaw komentarz poniżej — z przyjemnością usłyszę od Ciebie!
Dalsza lektura
Jeśli chcesz trochę bardziej poznać język CSCS, oto kilka artykułów, o których pisałem na ten temat:
- „Parser wyrażeń podziału i łączenia w języku C#”, MSDN Magazine (październik 2015 r.)
- „Konfigurowalne skrypty w C#”, MSDN Magazine (luty 2016)
- „Pisanie natywnych aplikacji mobilnych przy użyciu konfigurowalnego języka skryptów”, MSDN Magazine (luty 2018)
- „CSCS: dostosowane skrypty w C#”, GitHub
- „Tworzenie wieloplatformowych aplikacji natywnych z funkcjonalnym językiem skryptowym”, CODE Magazine
- „Zwięzłe wdrażanie niestandardowego języka” (eBook)
- „Zwięzłe pisanie natywnych aplikacji mobilnych w funkcjonalnym języku” (eBook)
Jako dodatkowy zasób polecam również przeczytanie, jak możesz poprawić wydajność CSCS, wstępnie kompilując jego funkcje.