diff options
| author | ketsuban_152 <webmaster@dangofacotry.net> | 2026-08-09 11:52:20 +0900 |
|---|---|---|
| committer | ketsuban_152 <webmaster@dangofacotry.net> | 2026-08-09 11:52:20 +0900 |
| commit | 3d0a7e4adce1e050e309d90d1ee73ff784b704df (patch) | |
| tree | fea2e81b8fd2921e7bbf9aadbbd40a7e364eef83 | |
| parent | 48f7b7c060f5a867fc8bac5a29202ffc6de9d4c4 (diff) | |
| download | dwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.tar.gz dwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.tar.bz2 dwgo-3d0a7e4adce1e050e309d90d1ee73ff784b704df.zip | |
modified: dwgo.conf
modified: src/MySock.cpp
modified: src/MySock.h
modified: src/localtemp.cpp
modified: src/localtemp.h
| -rw-r--r-- | dwgo.conf | 12 | ||||
| -rw-r--r-- | src/MySock.cpp | 104 | ||||
| -rw-r--r-- | src/MySock.h | 5 | ||||
| -rw-r--r-- | src/localtemp.cpp | 340 | ||||
| -rw-r--r-- | src/localtemp.h | 41 |
5 files changed, 299 insertions, 203 deletions
@@ -1,9 +1,11 @@ #[Stations] -AddStation LEMG:Malaga -AddStation LEVD:Valladolid -AddStation LEGR:Granada -AddStation LEBA:Cprdoba -AddStation LEMD:Madrid +# Station id is the 5-digit JMA station code. +# You can found this on the JMA Jmedas map. +AddStation 44132:Tokyo +AddStation 62078:Osaka +AddStation 51106:Nagoya +AddStation 14163:Sapporo +AddStation 82182:Fukuoka #[General configuration] update_interval=100 diff --git a/src/MySock.cpp b/src/MySock.cpp index 79e407d..6f73517 100644 --- a/src/MySock.cpp +++ b/src/MySock.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * File: MySock.cpp * - * Version: 0.2 * - * Date: 20081002 * + * Version: 0.4 * + * Date: 20260809 * * Author: Gaspar Fernández (helyo@totaki.com) * * * * Copyright (C) 2008 Gaspar Fernández * @@ -29,9 +29,12 @@ * Date Author Modification * 10.02.2008 Gaspar Fernández Initial release * 31.10.2010 Gaspar Fernández Bug Corrections + * 09.08.2026 Koudai Kudo fetch(1) for FreeBSD ********************************************************************************/ #include "MySock.h" #include <unistd.h> +#include <cstdio> +#include <cctype> /************************************************************* * Function: extract_key_value * ************************************************************* @@ -86,6 +89,7 @@ MySock::MySock() { this->error=NO_ERROR; this->connected=false; + this->use_external_fetch=false; } MySock::MySock(string uri) @@ -96,6 +100,7 @@ MySock::MySock(string uri) string protostr; string req; this->error=NO_ERROR; + this->use_external_fetch=false; string::size_type pos = uri.find("//", 0); string::size_type pos2; @@ -119,6 +124,15 @@ MySock::MySock(string uri) if (this->connected) this->SendData(req); } + else if (protostr=="https") + { + if(this->safe_url(uri)) + { + this->https_url=url; + this->use_external_fetch=true; + this->connection; + } + } else { this->error=NO_VALID_PROTOCOL; @@ -146,6 +160,85 @@ MySock::MySock(string uri) MySock::MySock(string server, int port) { this->MakeConnection(server, port); + this->use_external_fetch=false; +} + +/************************************************************* + * Method: is_safe_url +************************************************************** + * Description: Since https:// URIs end up inside a popen() * + * command, nad only allow a conservation character set. * + * + * Input: + * string url + *************************************************************/ +bool MySock::is_safe_url(const string &url) +{ + for (string::size_type i=0; i<url.length(); i++) + { + char c=url[i]; + if (c=='\'') + return false; + if (!(isalnum((unsigned char)c) || strchr(":/._-?=&%", c)!=NULL)) + return false; + } + return (url.length()>0) && (url.length()<2048); +} + +/************************************************************* + * Method: GetHTTPDataViaExternalFetch() * + ************************************************************* + * Description: * + * Retrieves an https:// URL by shelling out to fetch(1) * + * (FreeBSD base system) via popen(), since this class has * + * no TLS implementation of its own. fetch -o - writes the * + * response body to stdout, so unlike GetHTTPData()'s raw * + * socket path there are no HTTP headers to parse here - we * + * only know success/failure from fetch(1)'s exit status. * + * * + * Input: * + * Nothing (uses this->https_uri) * + * * + * Output: * + * HTTP_Request* - status is 200 on success, 502 if * + * fetch(1) reported an error (bad URL, network failure, * + * HTTP error status, etc); data holds the response body. * + * * + * Change History: * + * Date Author Modification * + * * + *************************************************************/ +HTTP_Request *MySock::GetHTTPDataViaExternalFetch() +{ + HTTP_Request *http; + string cmd; + FILE *pipe; + char buffer[4096]; + size_t n; + cmd=(string)HTTPS_FETCH_CMD+"'"+this->https_uri+"'"+" 2>/dev/null"; + pipe=popen(cmd.data(), "r"); + if (pipe==NULL) + { + this->error=CANT_RUN_FETCH; + return NULL; + } + http=new HTTP_Request; + http->data=""; + while ((n=fread(buffer, 1, sizeof(buffer), pipe))>0) + http->data.append(buffer, n); + int rc=pclose(pipe); + if ((rc==0) && (!http->data.empty())) + { + http->status=200; + http->statusstr="HTTP/1.1 200 OK (via fetch)"; + } + else + { + http->status=502; + http->statusstr="HTTP/1.1 502 Bad Gateway (fetch failed)"; + } + this->connected=false; // "Connection" (the popen pipe) is closed now + return http; } /************************************************************* @@ -293,6 +386,9 @@ HTTP_Request *MySock::GetHTTPData() char buf[255], buf2[255]; TKey_Value hdata; + if (this->use_external_fetch) + return this->GetHTTPDataViaExternalFetch(); + if (this->error==NO_ERROR) { txtData=this->GetTextData(); @@ -354,11 +450,11 @@ HTTP_Request *MySock::GetHTTPData() void MySock::closeConnection() { - if (connected) + if (connected && !use_external_fetch) { close(sockd); - connected=false; } + connected=false; } MySock::~MySock() diff --git a/src/MySock.h b/src/MySock.h index 132978c..4001429 100644 --- a/src/MySock.h +++ b/src/MySock.h @@ -20,7 +20,9 @@ using namespace std; #define CANT_READ_DATA 45 #define NO_VALID_PROTOCOL 50 #define NO_VALID_HEADERS 52 +#define CANT_RUN_FETCH 60 +#define HTTPS_FETCH_CMD "fetch -q -o - " #define CRLF "\r\n" struct HTTP_Request { @@ -55,8 +57,11 @@ public: private: int sockd, puerto; struct hostent *server; + bool use_external_fetch; + string https_uri; void MakeConnection (string server, int port); + HTTP_Request *GetHTTPDataViaExternalFetch(); }; TKey_Value extract_key_value(string str); diff --git a/src/localtemp.cpp b/src/localtemp.cpp index e8314bb..e6a5c2b 100644 --- a/src/localtemp.cpp +++ b/src/localtemp.cpp @@ -1,7 +1,7 @@ /******************************************************************************* * File: localtemp.cpp * - * Version: 0.3 * - * Date: 20080508 * + * Version: 0.4 * + * Date: 20260809 * * Author: Gaspar Fernández (helyo@totaki.com) * * * * Copyright (C) 2008 Gaspar Fernández * @@ -31,6 +31,7 @@ * Date (D.M.Y) Author Modification * 08.05.2008 Gaspar Fernández Initial release * 31.10.2010 Gaspar Fernández Bug Corrections + * 09.08.2026 Koudai Kudo Ported to JMA * ********************************************************************************/ @@ -48,7 +49,7 @@ using namespace std; * * * Input: * * char *metar - ICAO name locator (4 chars) * - * char location_name - Name of this location * + * char location_name - Amedas station code (5 digits) * * * * Change History: * * Date Author Modification * @@ -79,13 +80,10 @@ localtemp::localtemp(char *metar, char *location_name) * Date Author Modification * * * *************************************************************/ -void localtemp::set_temp(string tmp_str) +void localtemp::set_temp(double temp_c) { - char buf[8],buf3[8]; - // Format xx F (xx C) - sscanf(tmp_str.data(), "%s %s (%s", buf, buf3, buf3); // We don't want to store the middle F - this->celsius=atoi(buf3); - this->fahrenheit=atoi(buf); + this->celsius=(int)(temp_c+0.5); + this->fahrenheit=(int)(temp_c*9.0/5.0+32.0+0.5); } /************************************************************* @@ -95,143 +93,53 @@ void localtemp::set_temp(string tmp_str) * Extract humidity information. * * * * Input: * - * string hum - Temporary string containing humidity info. * + * int hum - Relative humidity, from the "humidity" field * + * of the Amedas JSON * * * * Change History: * * Date Author Modification * * * *************************************************************/ -void localtemp::set_humidity(string hum) +void localtemp::set_humidity(int hum) { - char buf[5]; // Be able to store a 100%\0 - // Format (xxx%) - sscanf(hum.data(), "%s", buf); - this->humidity=atoi(buf); + this->humidity=hum; } /************************************************************* * Method: get_ob_info * ************************************************************* * Description: * - * Extracts METAR encoded information. We find it in * - * the line starting with "ob: ". We parse this information * - * and fill in variables. Used for time and sky conditions. * + * Works out which theme to use from the 10 minute * + * precipitation figure and current temperature, since * + * Amedas doesn't report general sky conditions the way * + * METAR does. * * * * Input: * - * Nothing * +* double precip10m - 10 minute precipitation, in mm, as * + * read from the "precipitation10m" field of the Amedas * + * JSON (0 or negative means no precipitation reported) * * * * Change History: * * Date Author Modification * * * *************************************************************/ -void localtemp::get_ob_info() // ob: line stores METAR information. This info. is useful to know more exactly sky conditions. -{ // It stores also temperature, time and other info extracted by the decoded text - - size_t pos; - struct tm *localtm; - struct tm *remotetm; - remotetm =new struct tm; - string timeinfo; -try - { - timeinfo=this->ob.substr(6,this->ob.find(' ',6)-6); // DDHHMM[Z] Day Hour Minute Z (Zulu, Aviation Time Reference) - } catch (exception &e) - { - cout<<"fallo en 1"<<ob<<"***"<<endl; - } - localtm=localtime(&get_time); // tm of the fetch time - - // We need month and year of the time given. So let's calculate. -try - { - - remotetm->tm_mday=atoi(timeinfo.substr(0,2).data()); - remotetm->tm_hour=atoi(timeinfo.substr(2,2).data()); - remotetm->tm_min=atoi(timeinfo.substr(4,2).data()); - } catch (exception &e) +void localtemp::get_ob_info(double precip10m) +{ + mInfo.CB=false; + mInfo.fog=undef_fog; // Not reported by Amedas + if (precip10m>0.0) { - cout<<"fallo en 2"<<timeinfo<<"*"<<endl; + if (this->celsius<=2) + mInfo.rain=snow; + else + mInfo.rain=rain; + mInfo.sky=undef_sky; } - remotetm->tm_mon=localtm->tm_mon; - remotetm->tm_year=localtm->tm_year; - remotetm->tm_sec=0; - - if (remotetm->tm_mday==1) // Month of the data - { - if (remotetm->tm_mday!=localtm->tm_mday) - remotetm->tm_mon++; - if (remotetm->tm_mon==12) // It can't be 12!! - { - remotetm->tm_mon=0; - remotetm->tm_year++; - } - } - info_time=mktime(remotetm); - -try - { - this->ob=this->ob.substr(ob.find(' ',6)); // Extract ICAO station name and TIME INFO. - // It will prevent some errors. We could find any of these strings in that name. - - //this->ob=this->ob.substr(this->ob.find(this->metar)+5); - this->ob=this->ob.substr(0,this->ob.rfind('/')-2); // The info. we want is located betweet the last - - pos=this->ob.rfind('/'); // 2 slashes. The las one indicates temperature and dew point - if (pos!=string::npos) // The other one, may not exists but sometimes it does. - this->ob=this->ob.substr(this->ob.rfind('/')); - - } catch (exception &e) + else { - cout <<"Fallo en 3"<<ob<<"***"<<endl; - } - //Sky - if (this->ob.find("TCU")!=string::npos) - mInfo.sky=tcu; - else if ((this->ob.find("BKN")!=string::npos) || - (this->ob.find("OVC")!=string::npos)) - mInfo.sky=brkovc; - else if ((this->ob.find("FEW")!=string::npos) || - (this->ob.find("SCT")!=string::npos)) - mInfo.sky=cloudy; - else if ((this->ob.find("CAVOK")!=string::npos) || - (this->ob.find("SKC")!=string::npos) || - (this->ob.find("CLR")!=string::npos)) - mInfo.sky=clear; - else - mInfo.sky=undef_sky; - - mInfo.CB=(this->ob.find("CB")!=string::npos); - - - //Rain - if ((this->ob.find("DZ")!=string::npos) || - (this->ob.find("RA")!=string::npos)) - mInfo.rain=rain; - else if ((this->ob.find("SN")!=string::npos) || - (this->ob.find("SG")!=string::npos)) - mInfo.rain=snow; - else if ((this->ob.find("GS")!=string::npos) || - (this->ob.find("GR")!=string::npos)) - mInfo.rain=hail; - else mInfo.rain=undef_rain; - - //Fog - if ((this->ob.find("BR")!=string::npos) || - (this->ob.find("FG")!=string::npos)) - mInfo.fog=fog; - else if ((this->ob.find("DU")!=string::npos) || - (this->ob.find("DS")!=string::npos)) - mInfo.fog=dust; - else if ((this->ob.find("FC")!=string::npos) || - (this->ob.find("FU")!=string::npos) || - (this->ob.find("HZ")!=string::npos) || - (this->ob.find("SA")!=string::npos) || - (this->ob.find("SS")!=string::npos) || - (this->ob.find("VA")!=string::npos)) - mInfo.fog=other; - else - mInfo.fog=undef_fog; + mInfo.sky=clear; + } // The most important for the display theme is rain, then fog and then sky conditions if (mInfo.rain!=undef_rain) @@ -240,17 +148,6 @@ try { case rain: theme=RAINY_THEME; break; case snow: theme=SNOWY_THEME; break; - case hail: theme=HAIL_THEME; break; - default: break; - } - } - else if (mInfo.fog!=undef_fog) - { - switch (mInfo.fog) - { - case fog : theme=FOG_THEME; break; - case dust : theme=DUST_THEME; break; - case other: theme=PARTS_THEME; break; default: break; } } @@ -259,9 +156,6 @@ try switch (mInfo.sky) { case clear : theme=CLEAR_THEME; break; - case cloudy : theme=CLOUDY_THEME; break; - case brkovc : theme=BROKEN_THEME; break; - case tcu : theme=TCU_THEME; break; default: break; } } @@ -270,6 +164,99 @@ try } /************************************************************* + * Method: extract_json_number * + ************************************************************* + * Description: * + * Very small, purpose built JSON scraper. It looks for * + * "key":[ inside the given text and parses the first number * + * after it. Amedas values always look like "temp":[26.3,0] * + * (value, quality flag), so we only need the number before * + * the first comma or closing bracket. * + * * + * Input: * + * string json - Text to search in (one HHMM observation * + * block) * + * string key - Field name, e.g. "temp" * + * * + * Output: * + * double &value - Parsed number * + * bool - true if the key was found and a number was read * + * * + * Change History: * + * Date Author Modification * + * * + *************************************************************/ +bool localtemp::extract_json_number(const string &json, const string &key, double &value) +{ + string needle="\""+key+"\":["; + size_t pos=json.find(needle); + if (pos==string::npos) + return false; + pos+=needle.length(); + size_t endpos=json.find_first_of(",]", pos); + if (endpos==string::npos) + return false; + string numstr=json.substr(pos, endpos-pos); + if (numstr.empty() || numstr=="null") + return false; + value=atof(numstr.data()); + return true; +} +/************************************************************* + * Method: fetch_latest_time * + ************************************************************* + * Description: * + * Downloads AMEDAS_LATEST_URL, which returns a single * + * ISO-8601 JST timestamp such as * + * "2024-08-09T15:40:00+09:00", telling us which day's file * + * to request and which HHMM block inside it is the most * + * recent observation. * + * It also converts that JST timestamp into a real UTC * + * epoch (obs_utc), because dwgo.cpp's displaytemp() expects * + * info_time to be a plain UTC instant: it later does * + * "info_time + tm_diff" and feeds that into localtime() to * + * show it in the viewer's own local time zone. JMA gives us * + * JST (UTC+9) directly, so we build the calendar fields with * + * timegm() (which treats a struct tm as UTC, unlike * + * mktime() which would apply the *host*'s time zone) and * + * then subtract 9 hours to land back on true UTC. * + * * + * Output: * + * string &yyyymmdd - Date, e.g. "20240809" * + * string &hhmm - Time, e.g. "1540" * + * time_t &obs_utc - Observation instant as a UTC epoch * + * bool - true if we could fetch and parse the timestamp * + * * + * Change History: * + * Date Author Modification * + * * + *************************************************************/ +bool localtemp::fetch_latest_time(string &yyyymmdd, string &hhmm, time_t &obs_utc) +{ + MySock *skt; + HTTP_Request *http; + struct tm jst; + skt = new MySock((char*)AMEDAS_LATEST_URL); + http=skt->GetHTTPData(); + delete skt; + if (http==NULL || http->status!=200 || http->data.length()<19) + return false; + // Expected format: YYYY-MM-DDTHH:MM:SS+09:00 + string d=http->data; + yyyymmdd=d.substr(0,4)+d.substr(5,2)+d.substr(8,2); + hhmm=d.substr(11,2)+d.substr(14,2); + memset(&jst, 0, sizeof(jst)); + jst.tm_year=atoi(d.substr(0,4).data())-1900; + jst.tm_mon =atoi(d.substr(5,2).data())-1; + jst.tm_mday=atoi(d.substr(8,2).data()); + jst.tm_hour=atoi(d.substr(11,2).data()); + jst.tm_min =atoi(d.substr(14,2).data()); + jst.tm_sec =atoi(d.substr(17,2).data()); + obs_utc=timegm(&jst)-9*3600; // JST -> UTC (JMA has no DST, always UTC+9) + return true; +} + +/************************************************************* * Method: getInfo * ************************************************************* * Description: * @@ -284,65 +271,68 @@ try *************************************************************/ bool localtemp::getInfo() { - char *metar_fetch; // Get Metar data URL + char *point_fetch; // Amedas point data URL MySock *skt; HTTP_Request *http; - string::size_type pos, pos2; - TKey_Value datarl; + string yyyymmdd, hhmm; + time_t obs_utc; + double temp_c, hum, precip10m; this->error=0; // No error this->loaded=false; - metar_fetch= (char*) malloc(80); - string tmp; - sprintf(metar_fetch, METAR_URL, this->metar.data()); + if (!fetch_latest_time(yyyymmdd, hhmm, obs_utc)) + return false; - skt = new MySock(metar_fetch); + point_fetch= (char*) malloc(strlen(AMEDAS_POINT_URL)+this->metar.length()+yyyymmdd.length()+4); + sprintf(point_fetch, AMEDAS_POINT_URL, this->metar.data(), yyyymmdd.data()); + + skt = new MySock(point_fetch); verbsth(VERB_ASTTO, "Open connection: "); http=skt->GetHTTPData(); delete skt; // We don't need this anymore + free(point_fetch); if (http!=NULL) { if (http->status==200) { time(&get_time); // We got the file at this moment - pos=0; verbsth(VERB_ASTTO, "Get Data: "); - do + this->long_location=this->location_name; + // Locate the "HHMM": { ... } block matching the latest + // reported observation time and keep it (and everything + // after it, it doesn't matter) in this->ob, mirroring the + // way the old METAR "ob:" line used to be stored. + size_t blockpos=http->data.find("\""+hhmm+"\":"); + if (blockpos==string::npos) { - pos2=http->data.find("\n",pos+1); - if (pos==0) // Extracts the first line (Name of the Station) - { - this->long_location=http->data.substr(0, http->data.find("(",0)-1); - verbsth(VERB_ASTTO, "Station name: "+this->long_location); - } - if (pos2!=string::npos) - { - tmp = http->data.substr(pos,pos2-pos); - datarl.key=tmp.substr(0, tmp.find(":")); - datarl.value=tmp.substr(tmp.find(":")+1); - if (datarl.key=="Temperature") - this->set_temp(datarl.value); - else if (datarl.key=="Relative Humidity") - this->set_humidity(datarl.value); - else if (datarl.key=="Sky conditions") - this->sky=datarl.value; - else if (datarl.key=="ob") - this->ob=datarl.value; - // Search interesting data - pos=pos2+1; - } - } while (pos2!=string::npos); - verbsth(VERB_ASTTO, "Get ob info: "); - - get_ob_info(); // Get info from the METAR string - this->loaded=true; - verbsth(VERB_ASTTO, "Close connection: "); - + blockpos=http->data.rfind("\":{"); + if (blockpos!=string::npos) + blockpos=http->data.rfind("\"", blockpos-1); + } + if (blockpos!=string::npos) + { + this->ob=http->data.substr(blockpos); + if (extract_json_number(this->ob, "temp", temp_c)) + this->set_temp(temp_c); + if (extract_json_number(this->ob, "humidity", hum)) + this->set_humidity((int)hum); + if (!extract_json_number(this->ob, "precipitation10m", precip10m)) + precip10m=0.0; + this->info_time=obs_utc; // Actual observation instant, in UTC + get_ob_info(precip10m); // Work out theme from precipitation/temperature + this->loaded=true; + verbsth(VERB_ASTTO, "Close connection: "); + } + else + { + this->error=1; + verbsth(VERB_WARNING, "Couldn't find observation data in Amedas response."); + } } else { diff --git a/src/localtemp.h b/src/localtemp.h index 8bdb189..b4bbe0e 100644 --- a/src/localtemp.h +++ b/src/localtemp.h @@ -1,49 +1,50 @@ #include <string.h> #include <strings.h> #include "errors.h" +#include <time.h> // Old URL -//#define METAR_URL "http://weather.noaa.gov/pub/data/observations/metar/decoded/%s.TXT" - -// New URL #define METAR_URL "http://tgftp.nws.noaa.gov/data/observations/metar/decoded/%s.TXT" +// JMA Amedas data. +#define AMEDAS_LATEST_URL "https://www.jma.go.jp/bosai/amedas/data/latest_time.txt" +#define AMEDAS_POINT_URL "https://www.jma.go.jp/bosai/amedas/data/point/%s/%s_00.json" + class localtemp { public: enum ESky { - undef_sky, - clear, // CAVOK, SKC, CLR - cloudy, // FEW, SCT - brkovc, // Broken, Overcast (BKN, OVC) - tcu // TCU (Towering CUmulus) + clear, // Fine weather (no precipitation reported) + cloudy, // Reserved (not reported by Amedas) + brkovc, // Reserved (not reported by Amedas) + tcu // Reserved (not reported by Amedas) }; enum ERain { undef_rain, - rain, // Rainy (DZ, RA) - snow, // Snowy (SN, SG) - hail // Hail (GS, GR) + rain, // precipitation10m > 0, temperature > 2C + snow, // precipitation10m > 0, temperature <= 2C + hail // Reserved (not reported by Amedas) }; enum EFog { undef_fog, - fog, // (BR, FG) - dust, // (DU, DS) - other // (FC, FU, HZ, SA, SS, VA) + fog, // Reserved (not reported by Amedas) + dust, // Reserved (not reported by Amedas) + other // Reserved (not reported by Amedas) }; struct TMetar { ESky sky; - bool CB; // Cumulonimbus + bool CB; // Cumulonimbus (unused, kept for compatibility) ERain rain; EFog fog; }; std::string metar; std::string location_name; std::string long_location; - std::string ob; // ob: line, METAR info. + std::string ob; // Raw "HHMM" observation block extracted from the JSON int error, celsius, fahrenheit; int theme; // º theme to use short humidity; @@ -55,7 +56,9 @@ public: localtemp(char* metar, char* location_name); bool getInfo(); private: - void set_temp(std::string temp); - void set_humidity(std::string hum); - void get_ob_info(); + void set_temp(double temp_c); + void set_humidity(int hum); + void get_ob_info(double precip10m); + bool fetch_latest_time(std::string &yyyymmdd, std::string &hhmm, time_t &obs_utc); + bool extract_json_number(const std::string &json, const std::string &key, double &value); }; |
