| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747 | using System;using System.Collections;using System.Collections.Generic;using System.Data.Entity;using System.Data.Entity.Infrastructure;using System.Linq;using System.Net;using System.Web;using System.Web.Http;using System.Web.Http.Description;using DevExpress.Office.Utils;using iBemsDataService.Model;using iBemsDataService.Util;using System.Data.Entity.Validation;using System.Text;using System.Text.RegularExpressions;using iBemsDataService.Controllers.Bems.Formula;using System.Diagnostics;using iBemsDataService.Controllers.Bems.Common;using System.Transactions;namespace iBemsDataService.Controllers{    public class BemsFormulaController : ApiController    {        private iBemsEntities db;        private PointHistoryValueManager pointHistoryValueManager;        private static Regex regexParameter = new Regex(@"\b[A-Z]\b", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);        private static Regex regexFunction = new Regex(@"\$([\w]+).+\$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);        private static FormulaTableManager tableManager = new FormulaTableManager();        //private BemsMonitoringPointHistoryController pointHistoryController;        public BemsFormulaController()        {            db = new iBemsEntities();            pointHistoryValueManager = new PointHistoryValueManager(db);        }        public BemsFormulaController(iBemsEntities bemsDb)        {            db = bemsDb;        }        [ActionName("Calculation")]        public List<CalculationResult> GetBemsFormula()        {            Uri uri = Request.RequestUri;            var uriQuery = HttpUtility.ParseQueryString(uri.Query);            string uriSiteId = uriQuery.Get("SiteId");            string uriFacilityTypeId = uriQuery.Get("FacilityTypeId");            string uriFormulaId = uriQuery.Get("FormulaId");            string uriTimeIntervalType = uriQuery.Get("TimeIntervalType");            string uriStartDate = uriQuery.Get("StartDate");            string uriEndDate = uriQuery.Get("EndDate");            string uriFacilityCode = uriQuery.Get("FacilityCode");            int siteId = -1, facilityTypeId = -1, formulaId = -1, timeIntervalType = -1, facilityCode = -1;            DateTime startDate, endDate;            if (int.TryParse(uriSiteId, out siteId) == false ||                int.TryParse(uriFacilityTypeId, out facilityTypeId) == false ||                int.TryParse(uriFormulaId, out formulaId) == false ||                int.TryParse(uriTimeIntervalType, out timeIntervalType) == false ||                int.TryParse(uriFacilityCode, out facilityCode) == false ||                DateTime.TryParse(uriStartDate, out startDate) == false ||                DateTime.TryParse(uriEndDate, out endDate) == false)            {                throw new Exception(string.Format("Not Found FFFF Parameters: {0},{1},{2},{3},{4},{5},{6}",                    uriSiteId, uriFacilityTypeId, facilityCode, uriFormulaId, uriTimeIntervalType, uriStartDate, uriEndDate));            }            return GetBemsFormulaReal(siteId, facilityTypeId, facilityCode, formulaId, startDate, endDate, timeIntervalType);        }        public List<CalculationResult> GetBemsFormulaReal(int siteId, int facilityTypeId, int facilityCode, int formulaId, DateTime startDate, DateTime endDate, int timeIntervalType)        {            List<CalculationResult> list = new List<CalculationResult>();            BemsFormula formula = GetFormula(siteId, facilityTypeId, facilityCode, formulaId);            if (formula == null) return list;            TimeInterval timeInterval = (TimeInterval)timeIntervalType;            ArrayList parameters = new ArrayList();            MathCalculator calculator = new MathCalculator();            if (false == calculator.Compile(formula.Formula))            {                throw new Exception("Failed to compile formula: " + formula.Formula);            }            var match = regexParameter.Match(formula.Formula);            if (match.Success)            {                var queryParameters = db.BemsFormulaParameter.Where(x =>                    x.SiteId == siteId &&                    x.FacilityTypeId == facilityTypeId &&                        //x.FacilityCode == facilityCode &&                        //(facilityTypeId >= 24 || x.FacilityCode == facilityCode) && // hcLee 2015 04 21                    (facilityTypeId >= 100 || x.FacilityCode == facilityCode) && // hcLee 2015 04 28                    x.FormulaId == formulaId);                if (queryParameters == null || queryParameters.Any() == false)                {                    throw new Exception(string.Format("Not Found Formula Parameters: {0},{1},{2},{3}", siteId, facilityTypeId, facilityCode, formulaId));                }                List<MathParameterValue> parametersForArray = new List<MathParameterValue>();                foreach (var p in queryParameters)                {                    char parameter = p.ParameterId[0];                    var pValue = new MathParameterValue                    {                        Variable = (MathCalculator.Parameter)(parameter - 'A'),                        Value = 0,                    };                    parametersForArray.Add(pValue);                    var values = CreateCalculationValues(startDate, endDate, timeInterval);  //2015 04 22 hcLee                    var pointvalues = pointHistoryValueManager.GetPointValues(siteId, p.ParameterFacilityCode,                        p.ParameterPropertyId, timeInterval, startDate, endDate);                    if (pointvalues == null)                        continue;                    int index = 0;                    CalculationValue pp = null;                    CalculationValue v = null;                    for (var i = 0; i < values.Length; i++)                    {                        v = values[i];                        if (pp == null)                        {                            if (pointvalues.Length <= index) break;                            pp = pointvalues[index];                        }                        if (pp.DateTime == v.DateTime)                        {                            v.Value += pp.Value;                            pp = null;                            index++;                            if (pointvalues.Length <= index) break;                        }                    }                    parameters.Add(new CalculationParameter                    {                        ParameterValue = pValue,                        Values = values                    });                }                calculator.SetParameter(parametersForArray.ToArray());            }            {                var functions = calculator.GetFunctions();                if (functions.Any())                {                    foreach (var f in functions)                    {                        var tableValues = tableManager.GetTableValues(f.Function.FunctionName);                        if (tableValues == null)                        {                            throw new Exception("Not Found Function.");                        }                        f.Function.FunctionDelegate = (x) =>                        {                            //Trace.WriteLine( typeof( x ) );                            var index = Array.BinarySearch(tableValues, new FormulaTableManager.TableComparerValue { XValue = (double)x });                            if (index < 0)                            {                                index = ~index;                                if (index == 0)                                {                                    //return (decimal)tableValues[0].YValue;                                    return Math.Round((decimal)tableValues[0].YValue, 2);                                }                                else if (index == tableValues.Length)                                {                                    //return (decimal)tableValues[tableValues.Length - 1].YValue;                                    return Math.Round((decimal)tableValues[tableValues.Length - 1].YValue, 2);                                }                            }                            //return (decimal)tableValues[index].YValue;                            return Math.Round((decimal)tableValues[index].YValue, 2);                        };                    }                }            }            //Trace.WriteLine( calculator.Calculate() );            DateTime dateTime = DateTime.Now;            if (parameters.Count > 0)            {                var ps = parameters.Cast<CalculationParameter>().ToArray();                ParameterBox parameterBox = new ParameterBox(ps);                while (parameterBox.Sync())                {                    var value = calculator.Calculate();                    if (value != null)                    {                        list.Add(new CalculationResult                        {                            DateTime = parameterBox.DateTime,                            Value = Math.Round((double)value, 2)                            //Value = (double)value                        });                    }                    if (parameterBox.NextStage() == false)                    {                        break;                    }                }            }            else            {                /*       2015 06 12 hcLee 막음,  데이터가 하나도 없는 경우 현재시간으로 이상한 값이 한개 오는 현상                 var value = calculator.Calculate();                if( value != null )                {                    list.Add( new CalculationResult                    {                        DateTime = dateTime ,                        Value = (double)value                    } );                }     */            }            return list;        }        [ResponseType(typeof(void))]        [ActionName("ControlPoint")]        public IHttpActionResult PostControlPoint()        {            Uri uri = Request.RequestUri;            var uriQuery = HttpUtility.ParseQueryString(uri.Query);            string uriSiteId = uriQuery.Get("SiteId");            string uriFacilityTypeId = uriQuery.Get("FacilityTypeId");            string uriFacilityCode = uriQuery.Get("FacilityCode");            string uriFormulaId = uriQuery.Get("FormulaId");            string uriControlValue = uriQuery.Get("ControlValue");            int siteId = -1, facilityTypeId = -1, formulaId = -1, facilityCode = -1;            double controlValue = 0;            if (int.TryParse(uriSiteId, out siteId) == false ||                int.TryParse(uriFacilityTypeId, out facilityTypeId) == false ||                int.TryParse(uriFormulaId, out formulaId) == false ||                int.TryParse(uriFacilityCode, out facilityCode) == false ||                double.TryParse(uriControlValue, out controlValue) == false)            {                throw new Exception(string.Format("Not Found FFFF Parameters: {0},{1},{2},{3},{4} ", uriSiteId, uriFacilityTypeId, facilityCode, uriFormulaId, uriControlValue));            }            BemsFormula formula = GetFormula(siteId, facilityTypeId, facilityCode, formulaId);            if (formula == null) return StatusCode(HttpStatusCode.NoContent);            ArrayList parameters = new ArrayList();            MathCalculator calculator = new MathCalculator();            if (false == calculator.Compile(formula.Formula))            {                throw new Exception("Failed to compile formula: " + formula.Formula);            }            var match = regexParameter.Match(formula.Formula);            if (match.Success)            {                var queryParameters = db.BemsFormulaParameter.Where(x =>                    x.SiteId == siteId &&                    x.FacilityTypeId == facilityTypeId &&                        //x.FacilityCode == facilityCode &&                        //(facilityTypeId >= 24 || x.FacilityCode == facilityCode) && // hcLee 2015 04 21                    (facilityTypeId >= 100 || x.FacilityCode == facilityCode) && // hcLee 2015 04 28                    x.FormulaId == formulaId);                if (queryParameters.Any() == false)                {                    throw new Exception(string.Format("Not Found 2222 Parameters: {0},{1},{2},{3},{4}", siteId, facilityTypeId, facilityCode, formulaId, uriControlValue));                }                List<MathParameterValue> parametersForArray = new List<MathParameterValue>();                using (var transaction = new TransactionScope())                {                    foreach (var p in queryParameters)                    {                        char parameter = p.ParameterId[0];                        var pValue = new MathParameterValue                        {                            Variable = (MathCalculator.Parameter)(parameter - 'A'),                            Value = 0,                        };                        DateTime currentTime = DateTime.Now;                        try                        {                            {                                var control = new BemsControlPointHistory                                {                                    SiteId = siteId,                                    //FacilityTypeId = facilityTypeId,    // 2015 08 05 두소장과 통화중 일단 결정함.                                    FacilityTypeId = facilityTypeId,    // 2015 08 05 두소장과 통화중 일단 결정함.                                    FacilityCode = p.ParameterFacilityCode,                                    PropertyId = p.ParameterPropertyId,    // ???? hcLee                                    CreateDateTime = currentTime,                                    ControlValue = controlValue,                                    WriteServiceName = null,                                    ActionDateTime = null                                };                                db.BemsControlPointHistory.Add(control);                                db.SaveChanges();                            }                        }                        catch (Exception e)                        {                            Trace.WriteLine(e.Message);                            //throw e;                            throw;                        }                    }                    transaction.Complete();                }                //calculator.SetParameter(parametersForArray.ToArray());            }            return StatusCode(HttpStatusCode.NoContent);        }        private CalculationValue[] CreateCalculationValues(DateTime startDate, DateTime endDate, TimeInterval timeIntervalType)        {            DateTime datetime = startDate;            int count = GetCountInTimeRange(startDate, endDate, timeIntervalType);            CalculationValue[] values = new CalculationValue[count];            for (var i = 0; i < values.Length; i++)            {                values[i] = new CalculationValue                {                    DateTime = datetime,                    Value = 0                };                NextTimeFromTimeInterval(ref datetime, timeIntervalType);            }            return values;        }        private void NextTimeFromTimeInterval(ref DateTime datetime, TimeInterval timeIntervalType)        {            switch (timeIntervalType)            {                case TimeInterval.QuarterMin:                    datetime = datetime.AddMinutes(15);                    break;                case TimeInterval.Hour:                    datetime = datetime.AddHours(1);                    break;                case TimeInterval.Day:                    datetime = datetime.AddDays(1);                    break;                case TimeInterval.Month:                    datetime = datetime.AddMonths(1);                    break;                default: // TimeInterval.Year:                    datetime = datetime.AddYears(1);                    break;            }        }        private int GetCountInTimeRange(DateTime startDate, DateTime endDate, TimeInterval timeIntervalType)        {            TimeSpan ts = endDate - startDate;            switch (timeIntervalType)            {                case TimeInterval.QuarterMin:                    return (int)ts.TotalMinutes / 15 + 1;                case TimeInterval.Hour:                    return (int)ts.TotalHours + 1;                case TimeInterval.Day:                    return (int)ts.TotalDays + 1;                case TimeInterval.Month:                    {                        int count = 0;                        DateTime date = startDate;                        //while (date.Year <= endDate.Year && date.Month <= endDate.Month)                        while (date <= endDate)                        {                            count++;                            date = date.AddMonths(1);                        }                        return count;                    }                default: // TimeInterval.Year:                    {                        int count = 0;                        DateTime date = startDate;                        while (date.Year <= endDate.Year)                        {                            count++;                            date = date.AddYears(1);                        }                        return count;                    }            }        }        /*                [ActionName("ServiceType")]                public List<CalculationResult> GetBemsFormulaService()                {                    Uri uri = Request.RequestUri;                    var uriQuery = HttpUtility.ParseQueryString(uri.Query);                    string uriSiteId = uriQuery.Get("SiteId");                    string uriFacilityTypeId = uriQuery.Get("FacilityTypeId");                    string uriFormulaId = uriQuery.Get("FormulaId");                    string uriTimeIntervalType = uriQuery.Get("TimeIntervalType");                    string uriStartDate = uriQuery.Get("StartDate");                    string uriEndDate = uriQuery.Get("EndDate");                    string uriFacilityCode = uriQuery.Get("FacilityCode");                    string uriServiceTypeId = uriQuery.Get("ServiceTypeId");                    int siteId = -1, facilityTypeId = -1, formulaId = -1, timeIntervalType = -1, facilityCode = -1;                    int serviceTypeId = -1;                    DateTime startDate, endDate;                    if (int.TryParse(uriSiteId, out siteId) == false ||                        int.TryParse(uriFacilityTypeId, out facilityTypeId) == false ||                        int.TryParse(uriFormulaId, out formulaId) == false ||                        int.TryParse(uriTimeIntervalType, out timeIntervalType) == false ||                        int.TryParse(uriFacilityCode, out facilityCode) == false ||                        DateTime.TryParse(uriStartDate, out startDate) == false ||                        DateTime.TryParse(uriEndDate, out endDate) == false                        || int.TryParse(uriServiceTypeId, out serviceTypeId) == false)                    {                        throw new Exception(string.Format("Not Found Parameters: {0},{1},{2},{3},{4},{5},{6}, {7}",                            uriSiteId, uriFacilityTypeId, facilityCode, uriFormulaId, uriTimeIntervalType, uriStartDate, uriEndDate, uriServiceTypeId));                    }                    TimeInterval timeInterval = (TimeInterval)timeIntervalType;                    BemsFormula formula = GetFormula(siteId, facilityTypeId, facilityCode, formulaId);                    ArrayList parameters = new ArrayList();                    MathCalculator calculator = new MathCalculator();                    if (false == calculator.Compile(formula.Formula))                    {                        throw new Exception("Failed to compile formula: " + formula.Formula);                    }                    var match = regexParameter.Match(formula.Formula);                    if (match.Success)                    {                        var queryParameters = db.BemsFormulaParameter.Where(x =>                            x.SiteId == siteId &&                            x.FacilityTypeId == facilityTypeId &&                            x.FacilityCode == facilityCode &&                            x.FormulaId == formulaId);                        if (queryParameters.Any() == false)                        {                            throw new Exception(string.Format("Not Found Parameters: {0},{1},{2},{3}", siteId, facilityTypeId, facilityCode, formulaId));                        }                        List<MathParameterValue> parametersForArray = new List<MathParameterValue>();                        foreach (var p in queryParameters)                        {                            char parameter = p.ParameterId[0];                            var pValue = new MathParameterValue                            {                                Variable = (MathCalculator.Parameter)(parameter - 'A'),                                Value = 0,                            };                            parametersForArray.Add(pValue);                            var values = pointHistoryValueManager.GetPointValues(siteId, p.ParameterFacilityCode,                                p.ParameterPropertyId, timeInterval, startDate, endDate);                            parameters.Add(new CalculationParameter                            {                                ParameterValue = pValue,                                Values = values                            });                        }                        calculator.SetParameter(parametersForArray.ToArray());                    }                    {                        var functions = calculator.GetFunctions();                        if (functions.Any())                        {                            foreach (var f in functions)                            {                                var tableValues = tableManager.GetTableValues(f.Function.FunctionName);                                if (tableValues == null)                                {                                    throw new Exception("Not Found Function.");                                }                                f.Function.FunctionDelegate = (x) =>                                {                                    //Trace.WriteLine( typeof( x ) );                                    var index = Array.BinarySearch(tableValues, new FormulaTableManager.TableComparerValue { XValue = (double)x });                                    if (index < 0)                                    {                                        index = ~index;                                        if (index == 0)                                        {                                            return (decimal)tableValues[0].YValue;                                        }                                        else if (index == tableValues.Length)                                        {                                            return (decimal)tableValues[tableValues.Length - 1].YValue;                                        }                                    }                                    return (decimal)tableValues[index].YValue;                                };                            }                        }                    }                    //Trace.WriteLine( calculator.Calculate() );                    List<CalculationResult> list = new List<CalculationResult>();                    DateTime dateTime = DateTime.Now;                    if (parameters.Count > 0)                    {                        var ps = parameters.Cast<CalculationParameter>().ToArray();                        ParameterBox parameterBox = new ParameterBox(ps);                        while (parameterBox.Sync())                        {                            var value = calculator.Calculate();                            if (value != null)                            {                                list.Add(new CalculationResult                                {                                    DateTime = parameterBox.DateTime,                                    Value = (double)value                                });                            }                            if (parameterBox.NextStage() == false)                            {                                break;                            }                        }                    }                    else                    {                        var value = calculator.Calculate();                        if (value != null)                        {                            list.Add(new CalculationResult                            {                                DateTime = dateTime,                                Value = (double)value                            });                        }                    }                    return list;                }        */        [ResponseType(typeof(void))]        public IHttpActionResult PostBemsFormula(FormulaData formula)        {            if (!ModelState.IsValid)            {                return BadRequest(ModelState);            }            var queryFormula = db.BemsFormula.Where(x =>                x.SiteId == formula.SiteId &&                x.FacilityTypeId == formula.FacilityTypeId &&                x.FacilityCode == formula.FacilityCode &&                x.FormulaId == formula.FormulaId);            BemsFormula f = null;            bool isNewFormula = false;            if (queryFormula.Any())            {                f = queryFormula.First();            }            else            {                f = new BemsFormula();                isNewFormula = true;            }            f.SiteId = formula.SiteId;            f.FacilityTypeId = formula.FacilityTypeId;            f.FacilityCode = formula.FacilityCode;            f.FormulaId = formula.FormulaId;            f.Formula = formula.Formula;            if (isNewFormula)            {                db.BemsFormula.Add(f);            }            var parameterQuery = db.BemsFormulaParameter.Where(x =>                x.SiteId == formula.SiteId &&                x.FacilityTypeId == formula.FacilityTypeId &&                x.FacilityCode == formula.FacilityCode &&                x.FormulaId == formula.FormulaId);            var qm = new QueryManager<BemsFormulaParameter>(db);            try            {                qm.DeleteAndInsert(db.BemsFormulaParameter, formula.Paramters, parameterQuery);                if (formula.Paramters.Count == 0)                {                    db.BemsFormula.Remove(queryFormula.FirstOrDefault());                }            }            catch (DbEntityValidationException ex)            {                StringBuilder sb = new StringBuilder();                foreach (var failure in ex.EntityValidationErrors)                {                    sb.AppendFormat("{0} failed validation\n", failure.Entry.Entity.GetType());                    foreach (var error in failure.ValidationErrors)                    {                        sb.AppendFormat("- {0} : {1}", error.PropertyName, error.ErrorMessage);                        sb.AppendLine();                    }                }                return BadRequest(sb.ToString());                //                throw ex;            }            catch (Exception) { throw; }            db.SaveChanges();            return StatusCode(HttpStatusCode.NoContent);        }        protected override void Dispose(bool disposing)        {            if (disposing)            {                db.Dispose();            }            base.Dispose(disposing);        }        //private BemsFormula GetFormula( int siteId , int facilityTypeId ,        public BemsFormula GetFormula(int siteId, int facilityTypeId,            int facilityCode, int formulaId)        {            /*            var queryFormula = db.BemsFormula.Where( x =>                x.SiteId == siteId &&                x.FacilityTypeId == facilityTypeId &&                //x.FacilityCode == facilityCode) &&                //(facilityTypeId >= 24 || x.FacilityCode == facilityCode) && // hcLee 2015 04 16                (facilityTypeId >= 100 || x.FacilityCode == facilityCode) && // hcLee 2015 04  28                x.FormulaId == formulaId );*/            var queryFormula = from f in db.BemsFormula                               where f.SiteId == siteId && f.FacilityTypeId == facilityTypeId &&                                   (facilityTypeId >= 100 || f.FacilityCode == facilityCode) && f.FormulaId == formulaId                               select f;            if (queryFormula == null || queryFormula.Any() == false)            {                //throw new Exception(string.Format("Not Found FFFF Formula: {0},{1},{2},{3}", siteId, facilityTypeId, facilityCode, formulaId));                //throw new Exception(string.Format("설비 {0}의 {1}번 성능분석 수식이 필요합니다!", facilityCode, formulaId));                //throw new Exception(string.Format("성능분석 수식이 필요합니다!", facilityCode, formulaId));                //return null;                //throw new Exception("성능분석 수식이 필요합니다!");                //throw new Exception("Make Fornula please");                return null;            }            //if (queryFormula == null || queryFormula.Any() == false) return null;            return queryFormula.Single();        }    }    public class CalculationParameter    {        public MathParameterValue ParameterValue { get; set; }        //public string FacilityCode { get; set; }        //public int PropertyId { get; set; }        public CalculationValue[] Values { get; set; }        public DateTime SetValueFromIndex(int index)        {            var value = this.Values[index];            this.ParameterValue.Value = (decimal)value.Value;            return value.DateTime;        }    };    public class FormulaData    {        public int SiteId { get; set; }        public int FacilityTypeId { get; set; }        public int FacilityCode { get; set; }        public int FormulaId { get; set; }        public string Formula { get; set; }        public List<FormulaParameterData> Paramters { get; set; }    };    public class FormulaParameterData    {        public int SiteId { get; set; }        public int FacilityTypeId { get; set; }        public int FacilityCode { get; set; }        public int FormulaId { get; set; }        public string ParameterId { get; set; }        public int ParameterFacilityCode { get; set; }        public int ParameterPropertyId { get; set; }    };}
 |