private async Task checkMetaUTC()
        {
            if (_lastMetaUTC + META_UTC_DELAY < YAPI.GetTickCount())
            {
                HidOutputReport outReport = Hid.CreateOutputReport();
                YUSBPkt.imm_FormatMetaUTC(outReport, true);
                var u = await Hid.SendOutputReportAsync(outReport);

                if (u != 65)
                {
                    _devState = DevState.IOError;
                    throw new YAPI_Exception(YAPI.IO_ERROR, "Unable to send Start PKT");
                }

                _lastMetaUTC = YAPI.GetTickCount();
            }
        }
        public override string ToString()
        {
            string res = "Request " + _debug_name + "\n";

            res += _isDone ? "done" : "working";
            if (_asyncResult != null)
            {
                res += "async";
            }

            res += " res_len=" + _response_size;
            ulong now = YAPI.GetTickCount();

            res += " tmout=" + (_timeout - _tm_start);
            res += " start=" + (_tm_start - now);
            return(res);
        }
Beispiel #3
0
        // Change the value of an attribute on a device, and update cache on the fly
        // Note: the function cache is a typed (parsed) cache, contrarily to the agnostic device cache
        protected internal virtual async Task <int> _setAttr(string attr, string newval)
        {
            if (newval == null)
            {
                throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Undefined value to set for attribute " + attr);
            }
            string attrname = YAPIContext.imm_escapeAttr(attr);
            string extra    = "/" + attrname + "?" + attrname + "=" + YAPIContext.imm_escapeAttr(newval) + "&.";

            await _devRequest(extra);

            if (_cacheExpiration != 0)
            {
                _cacheExpiration = YAPI.GetTickCount();
            }
            return(YAPI.SUCCESS);
        }
        internal async Task <byte[]> GetResponse()
        {
            Task <byte[]> task      = _tcs.Task;
            ulong         now       = YAPI.GetTickCount();
            int           msTimeout = (int)(_timeout - now);
            Task          endedTask = await Task.WhenAny(task, Task.Delay(msTimeout));

            if (endedTask != task)
            {
                _asyncResult?.Invoke(_asyncContext, imm_RemoveHeader(_response), YAPI.TIMEOUT, "USB request " + _debug_name + " did not finished in time");
                throw new YAPI_Exception(YAPI.TIMEOUT, "USB request " + _debug_name + " did not finished in time");
            }

            byte[] response = task.Result;
            var    res      = imm_RemoveHeader(response);

            return(res);
        }
Beispiel #5
0
        // Get the whole REST API string for a device, from cache if possible
        internal virtual async Task <YJSONObject> requestAPI()
        {
            if (_cache_expiration > YAPI.GetTickCount())
            {
                return(_cache_json);
            }

            string request;

            if (_cache_json == null)
            {
                request = "GET /api.json \r\n\r\n";
            }
            else
            {
                string fw = _cache_json.getYJSONObject("module").getString("firmwareRelease");
                request = "GET /api.json?fw=" + YAPIContext.imm_escapeAttr(fw) + " \r\n\r\n";
            }

            string yreq;

            try {
                yreq = await requestHTTPSyncAsString(request, null);
            } catch (YAPI_Exception) {
                await _hub.updateDeviceListAsync(true);

                yreq = await requestHTTPSyncAsString(request, null);
            }

            YJSONObject cache_json;

            try {
                cache_json = new YJSONObject(yreq);
                cache_json.parseWithRef(_cache_json);
            } catch (Exception ex) {
                _cache_json = null;
                throw new YAPI_Exception(YAPI.IO_ERROR, "Request failed, could not parse API (" + ex.Message + ")");
            }

            this._cache_expiration = YAPI.GetTickCount() + await YAPI.imm_GetYCtx().GetCacheValidity();

            this._cache_json = cache_json;
            return(cache_json);
        }
        public YRequest(byte[] requestBytes, YGenericHub.RequestAsyncResult asyncResult, object asyncContext, ulong maxTime)
        {
            _response      = new byte[1024];
            _response_size = 0;
            _isDone        = false;
            _debug_name    = YAPI.DefaultEncoding.GetString(requestBytes);
            int debug_pos = _debug_name.IndexOf('\r');

            if (debug_pos > 0)
            {
                _debug_name = _debug_name.Substring(0, debug_pos);
            }

            _asyncResult  = asyncResult;
            _asyncContext = asyncContext;
            _tm_start     = YAPI.GetTickCount();
            _timeout      = _tm_start + maxTime;
            _tcs          = new TaskCompletionSource <byte[]>();
        }
        internal override async Task <byte[]> devRequestSync(YDevice device, string req_first_line, byte[] req_head_and_body, uint mstimeout, YGenericHub.RequestProgress progress, object context)
        {
            ulong start = YAPI.GetTickCount();

            if (!_httpReqByDev.ContainsKey(device))
            {
                _httpReqByDev[device] = new YHTTPRequest(_hub, "Device " + device.SerialNumber);
            }

            YHTTPRequest req = _httpReqByDev[device];

            byte[] result = await req.RequestSync(req_first_line, req_head_and_body, mstimeout);

            ulong stop = YAPI.GetTickCount();

            //Debug.WriteLine(string.Format("SyncRes on {0} took {1}ms", device.SerialNumber, stop - start));

            return(result);
        }
Beispiel #8
0
        internal void imm_AppendResponseData(byte[] data)
        {
            int length = _responseData.Length;

            if (_responseLen + data.Length > length)
            {
                byte[] tmp = new byte[length * 2];
                Array.Copy(_responseData, 0, tmp, 0, _responseLen);
                Array.Copy(data, 0, tmp, _responseLen, data.Length);
                _responseLen += data.Length;
                _responseData = tmp;
            }
            else
            {
                Array.Copy(data, 0, _responseData, _responseLen, data.Length);
                _responseLen += data.Length;
            }

            _tmIn = YAPI.GetTickCount();
        }
        internal async Task EnsureLastRequestDone()
        {
            if (_currentReq != null)
            {
                ulong now     = YAPI.GetTickCount();
                ulong timeout = _startRequestTime + _requestTimeout;
                if (timeout <= now)
                {
                    throw new YAPI_Exception(YAPI.TIMEOUT, "Last Http request did not finished");
                }
                int  msTimeout = (int)(timeout - now);
                Task task      = await Task.WhenAny(_currentReq, Task.Delay(msTimeout));

                if (task != _currentReq)
                {
                    throw new YAPI_Exception(YAPI.TIMEOUT, "Last Http request did not finished");
                }
                _currentReq = null;
            }
        }
Beispiel #10
0
        internal override async Task updateDeviceListAsync(bool forceupdate)
        {
            ulong now = YAPI.GetTickCount();

            if (forceupdate)
            {
                _devListExpires = 0;
            }
            if (_devListExpires > now)
            {
                return;
            }

            await _ywatcher.ImediateEnum();

            List <YUSBDevice> devices = _ywatcher.imm_GetUsableDevices();
            //_yctx._Log(string.Format("{0} devices usable\n",devices.Count));
            Dictionary <string, List <YPEntry> > yellowPages = new Dictionary <string, List <YPEntry> >();
            List <WPEntry> whitePages = new List <WPEntry>();

            foreach (YUSBDevice d in devices)
            {
                whitePages.Add(d.imm_GetWhitesPagesEntry());
                d.imm_UpdateYellowPages(yellowPages);
            }
            // Reindex all devices from white pages
            for (int i = 0; i < whitePages.Count; i++)
            {
                _serialByYdx[i] = whitePages[i].SerialNumber;
            }
            await updateFromWpAndYp(whitePages, yellowPages);

            // reset device list cache timeout for this hub
            now             = YAPI.GetTickCount();
            _devListExpires = now + 500;
        }
Beispiel #11
0
 internal virtual async void _parse(YJSONObject json, ulong msValidity)
 {
     _cacheExpiration = YAPI.GetTickCount() + msValidity;
     imm_parseAttr(json);
     await _parserHelper();
 }
        internal override async Task updateDeviceListAsync(bool forceupdate)
        {
            ulong now = YAPI.GetTickCount();

            if (forceupdate)
            {
                _devListExpires = 0;
            }
            if (_devListExpires > now)
            {
                return;
            }
            if (!_notificationHandler.Connected)
            {
                if (_reportConnnectionLost)
                {
                    throw new YAPI_Exception(YAPI.TIMEOUT, "hub " + _http_params.Url + " is not reachable");
                }
                else
                {
                    return;
                }
            }

            string json_data;

            try {
                byte[] data = await _notificationHandler.hubRequestSync("GET /api.json", null,
                                                                        YIO_DEFAULT_TCP_TIMEOUT);

                json_data = YAPI.DefaultEncoding.GetString(data);
            } catch (YAPI_Exception) {
                if (_reportConnnectionLost)
                {
                    throw;
                }
                return;
            }

            Dictionary <string, List <YPEntry> > yellowPages = new Dictionary <string, List <YPEntry> >();
            List <WPEntry> whitePages = new List <WPEntry>();

            YJSONObject loadval = new YJSONObject(json_data);

            loadval.parse();
            if (!loadval.has("services") || !loadval.getYJSONObject("services").has("whitePages"))
            {
                throw new YAPI_Exception(YAPI.INVALID_ARGUMENT, "Device " + _http_params.Host + " is not a hub");
            }
            _serial = loadval.getYJSONObject("module").getString("serialNumber");
            YJSONArray  whitePages_json  = loadval.getYJSONObject("services").getYJSONArray("whitePages");
            YJSONObject yellowPages_json = loadval.getYJSONObject("services").getYJSONObject("yellowPages");

            if (loadval.has("network"))
            {
                string adminpass = loadval.getYJSONObject("network").getString("adminPassword");
                _writeProtected = adminpass.Length > 0;
            }
            // Reindex all functions from yellow pages
            //HashMap<String, Boolean> refresh = new HashMap<String, Boolean>();
            List <string> keys = yellowPages_json.keys();

            foreach (string classname in keys)
            {
                YJSONArray     yprecs_json = yellowPages_json.getYJSONArray(classname);
                List <YPEntry> yprecs_arr  = new List <YPEntry>(yprecs_json.Length);
                for (int i = 0; i < yprecs_json.Length; i++)
                {
                    YPEntry yprec = new YPEntry(yprecs_json.getYJSONObject(i));
                    yprecs_arr.Add(yprec);
                }
                yellowPages[classname] = yprecs_arr;
            }

            _serialByYdx.Clear();
            // Reindex all devices from white pages
            for (int i = 0; i < whitePages_json.Length; i++)
            {
                YJSONObject jsonObject = whitePages_json.getYJSONObject(i);
                WPEntry     devinfo    = new WPEntry(jsonObject);
                int         index      = jsonObject.getInt("index");
                _serialByYdx[index] = devinfo.SerialNumber;
                whitePages.Add(devinfo);
            }
            await updateFromWpAndYp(whitePages, yellowPages);

            // reset device list cache timeout for this hub
            now             = YAPI.GetTickCount();
            _devListExpires = now + _devListValidity;
        }
Beispiel #13
0
 internal void imm_reportDataSent()
 {
     _tmOut = YAPI.GetTickCount();
 }
        private async Task _processMore(int start)
        {
            if (start == 0)
            {
                return;
            }
            imm_reportprogress(0, "Firmware update started");
            YFirmwareFile firmware;

            try {
                //1% -> 5%
                if (_firmwarepath.StartsWith("www.yoctopuce.com") || _firmwarepath.StartsWith("http://www.yoctopuce.com"))
                {
                    this.imm_reportprogress(1, "Downloading firmware");
                    byte[] bytes = await YFirmwareUpdate._downloadfile(_firmwarepath);

                    firmware = YFirmwareFile.imm_Parse(_firmwarepath, bytes);
                }
                else
                {
                    imm_reportprogress(1, "Loading firmware");
                    firmware = YFirmwareUpdate._loadFirmwareFile(_firmwarepath);
                }

                //5% -> 10%
                imm_reportprogress(5, "check if module is already in bootloader");
                YGenericHub hub    = null;
                YModule     module = YModule.FindModuleInContext(_yctx, _serial + ".module");
                if (await module.isOnline())
                {
                    YDevice yDevice = await module.getYDevice();

                    hub = yDevice.Hub;
                }
                else
                {
                    // test if already in bootloader
                    foreach (YGenericHub h in _yctx._hubs)
                    {
                        List <string> bootloaders = await h.getBootloaders();

                        if (bootloaders.Contains(_serial))
                        {
                            hub = h;
                            break;
                        }
                    }
                }
                if (hub == null)
                {
                    imm_reportprogress(-1, "Device " + _serial + " is not detected");
                    return;
                }

                await hub.firmwareUpdate(_serial, firmware, _settings, imm_firmware_progress);

                //80%-> 98%
                imm_reportprogress(80, "wait to the device restart");
                ulong timeout = YAPI.GetTickCount() + 60000;
                await module.clearCache();

                while (!await module.isOnline() && timeout > YAPI.GetTickCount())
                {
                    await Task.Delay(5000);

                    try {
                        await _yctx.UpdateDeviceList();
                    } catch (YAPI_Exception) {
                    }
                }
                if (await module.isOnline())
                {
                    if (_settings != null)
                    {
                        await module.set_allSettingsAndFiles(_settings);

                        await module.saveToFlash();
                    }
                    imm_reportprogress(100, "Success");
                }
                else
                {
                    imm_reportprogress(-1, "Device did not reboot correctly");
                }
            } catch (YAPI_Exception e) {
                imm_reportprogress(e.errorType, e.Message);
                Debug.WriteLine(e.ToString());
                Debug.Write(e.StackTrace);
            }
        }
Beispiel #15
0
        private async Task <WSRequest> sendRequest(string req_first_line, byte[] req_head_and_body, int tcpchanel, bool async, YGenericHub.RequestProgress progress, Object context)
        {
            WSRequest request;
            string    debug = req_first_line.Trim();

            byte[] full_request;
            byte[] req_first_lineBytes;
            if (req_head_and_body == null)
            {
                req_first_line     += "\r\n\r\n";
                req_first_lineBytes = YAPI.DefaultEncoding.GetBytes(req_first_line);
                full_request        = req_first_lineBytes;
            }
            else
            {
                req_first_line     += "\r\n";
                req_first_lineBytes = YAPI.DefaultEncoding.GetBytes(req_first_line);
                full_request        = new byte[req_first_lineBytes.Length + req_head_and_body.Length];
                Array.Copy(req_first_lineBytes, 0, full_request, 0, req_first_lineBytes.Length);
                Array.Copy(req_head_and_body, 0, full_request, req_first_lineBytes.Length, req_head_and_body.Length);
            }

            ulong timeout = YAPI.GetTickCount() + WS_REQUEST_MAX_DURATION;

            while ((_connectionState != ConnectionState.CONNECTED && _connectionState != ConnectionState.DEAD))
            {
                if (timeout < YAPI.GetTickCount())
                {
                    if (_connectionState != ConnectionState.CONNECTED && _connectionState != ConnectionState.CONNECTING)
                    {
                        throw new YAPI_Exception(YAPI.IO_ERROR, "IO error with hub");
                    }
                    else
                    {
                        throw new YAPI_Exception(YAPI.TIMEOUT, "Last request did not finished correctly");
                    }
                }

                if (_connectionState == ConnectionState.DEAD)
                {
                    throw new YAPI_Exception(_session_errno, _session_error);
                }
            }

            if (async)
            {
                request = new WSRequest(debug, tcpchanel, _nextAsyncId++, full_request);
                if (_nextAsyncId >= 127)
                {
                    _nextAsyncId = 48;
                }
            }
            else
            {
                request = new WSRequest(debug, tcpchanel, full_request, progress, context);
            }

            _workingRequests[tcpchanel].Enqueue(request);

            //todo: handle timeout
            await processRequests(request);

            if (request.ErrorCode != YAPI.SUCCESS)
            {
                throw new YAPI_Exception(request.ErrorCode, request.ErrorMsg);
            }

            return(request);
        }
Beispiel #16
0
        /*
         *   look through all pending request if there is some data that we can send
         *
         */
        private async Task processRequests(WSRequest request)
        {
            int tcpchan = request.Channel;

            int throttle_start = request.ReqPosition;
            int throttle_end   = request.ReqSize;

            if (throttle_end > 2108 && _remoteVersion >= YGenericHub.USB_META_WS_PROTO_V2 && tcpchan == 0)
            {
                // Perform throttling on large uploads
                if (request.ReqPosition == 0)
                {
                    // First chunk is always first multiple of full window (124 bytes) above 2KB
                    throttle_end = 2108;
                    // Prepare to compute effective transfer rate
                    _lastUploadAckBytes[tcpchan] = 0;
                    _lastUploadAckTime[tcpchan]  = 0;
                    // Start with initial RTT based estimate
                    _uploadRate = (int)(_tcpMaxWindowSize * 1000 / (int)_tcpRoundTripTime);
                }
                else if (_lastUploadAckTime[tcpchan] == 0)
                {
                    // first block not yet acked, wait more
                    throttle_end = 0;
                }
                else
                {
                    // adapt window frame to available bandwidth
                    long  bytesOnTheAir = request.ReqPosition - _lastUploadAckBytes[tcpchan];
                    ulong timeOnTheAir  = YAPI.GetTickCount() - _lastUploadAckTime[tcpchan];
                    int   uploadRate    = _uploadRate;
                    int   toBeSent      = (int)(2 * uploadRate + 1024 - bytesOnTheAir + (uploadRate * (int)timeOnTheAir / 1000));
                    if (toBeSent + bytesOnTheAir > DEFAULT_TCP_MAX_WINDOW_SIZE)
                    {
                        toBeSent = (int)(DEFAULT_TCP_MAX_WINDOW_SIZE - bytesOnTheAir);
                    }

                    WSLOG(string.Format("throttling: {0} bytes/s ({1} + {2} = {3})", _uploadRate, toBeSent, bytesOnTheAir, bytesOnTheAir + toBeSent));
                    if (toBeSent < 64)
                    {
                        ulong waitTime = (ulong)(1000 * (128 - toBeSent) / _uploadRate);
                        if (waitTime < 2)
                        {
                            waitTime = 2;
                        }
                        //_next_transmit_tm = YAPI.GetTickCount() + waitTime;
                        WSLOG(string.Format("WS: {0} sent {1}ms ago, waiting {2}ms...", bytesOnTheAir, timeOnTheAir, waitTime));
                        throttle_end = 0;
                    }

                    if (throttle_end > request.ReqPosition + toBeSent)
                    {
                        // when sending partial content, round up to full frames
                        if (toBeSent > 124)
                        {
                            toBeSent = (toBeSent / 124) * 124;
                        }

                        throttle_end = request.ReqPosition + toBeSent;
                    }
                }
            }

            while (request.ReqPosition < throttle_end)
            {
                IBuffer data;
                int     datalen = throttle_end - request.ReqPosition;
                if (datalen > MAX_DATA_LEN)
                {
                    datalen = MAX_DATA_LEN;
                }

                if (request.imm_isAsync() && (request.ReqPosition + datalen == request.ReqSize))
                {
                    if (datalen == MAX_DATA_LEN)
                    {
                        // last frame is already full we must send the async close in another one
                        data = request.imm_GetRequestData(request.ReqPosition, datalen);
                        await Send_WSStream(_webSock, YGenericHub.YSTREAM_TCP, tcpchan, data, 0);

                        //WSLOG(string.Format("ws_req:{0}: sent {1} bytes on chan{2} ({3}/{4})", request, datalen, tcpchan, request.ReqPosition, request.ReqSize));
                        request.imm_reportDataSent();
                        request.ReqPosition += datalen;
                        datalen              = 0;
                    }

                    data = request.imm_GetRequestData(request.ReqPosition, datalen);
                    await Send_WSStream(_webSock, YGenericHub.YSTREAM_TCP_ASYNCCLOSE, tcpchan, data, request.AsyncId);

                    //WSLOG(string.Format("ws_req:{0}: sent async close {1}", request, request.AsyncId));
                    request.ReqPosition += datalen;
                }
                else
                {
                    data = request.imm_GetRequestData(request.ReqPosition, datalen);
                    await Send_WSStream(_webSock, YGenericHub.YSTREAM_TCP, tcpchan, data, 0);

                    request.imm_reportDataSent();
                    //WSLOG(string.Format("ws_req:{0}: sent {1} bytes on chan{2} ({3}/{4})", request, datalen, tcpchan, request.ReqPosition, request.ReqSize));
                    request.ReqPosition += datalen;
                }
            }

            if (request.ReqPosition < request.ReqSize)
            {
                int sent = request.ReqPosition - throttle_start;
                // not completely sent, cannot do more for now
                if (sent > 0 && _uploadRate > 0)
                {
                    ulong waitTime = (ulong)(1000 * sent / _uploadRate);
                    if (waitTime < 2)
                    {
                        waitTime = 2;
                    }
                    //_next_transmit_tm = YAPI.GetTickCount() + waitTime;
                    WSLOG(string.Format("Sent {0}bytes, waiting {1}ms...", sent, waitTime));
                }
                else
                {
                    //_next_transmit_tm = YAPI.GetTickCount() + 100;
                }
            }
        }
Beispiel #17
0
        //The MessageReceived event handler.
        private async void WebSock_MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args)
        {
            try {
                DataReader messageReader = args.GetDataReader();
                messageReader.ByteOrder = ByteOrder.LittleEndian;
                WSRequest workingRequest;
                uint      messageSize = messageReader.UnconsumedBufferLength;
                byte      first_byte  = messageReader.ReadByte();
                int       tcpChanel   = first_byte & 0x7;
                int       ystream     = (first_byte & 0xff) >> 3;

                switch (ystream)
                {
                case YGenericHub.YSTREAM_TCP_NOTIF:
                    if (_firstNotif)
                    {
                        if (!_hub._http_params.imm_hasAuthParam())
                        {
                            _connectionState = ConnectionState.CONNECTED;
                            _firstNotif      = false;
                        }
                        else
                        {
                            return;
                        }
                    }

                    byte[] chars = new byte[messageReader.UnconsumedBufferLength];
                    messageReader.ReadBytes(chars);
                    String tcpNotif = YAPI.DefaultEncoding.GetString(chars);
                    decodeTCPNotif(tcpNotif);
                    break;

                case YGenericHub.YSTREAM_EMPTY:
                    return;

                case YGenericHub.YSTREAM_TCP_ASYNCCLOSE:
                    workingRequest = _workingRequests[tcpChanel].Peek();
                    if (workingRequest != null && messageReader.UnconsumedBufferLength >= 1)
                    {
                        uint contentSize = messageReader.UnconsumedBufferLength - 1;
                        //todo: try to copy data more efficently
                        byte[] data = new byte[contentSize];
                        if (contentSize > 0)
                        {
                            messageReader.ReadBytes(data);
                        }

                        int asyncId = messageReader.ReadByte();
                        if (workingRequest.AsyncId != asyncId)
                        {
                            _hub._yctx._Log("WS: Incorrect async-close signature on tcpChan " + tcpChanel + "\n");
                            return;
                        }

                        workingRequest.imm_AppendResponseData(data);
                        workingRequest.imm_close(YAPI.SUCCESS, "");
                        _workingRequests[tcpChanel].Dequeue();
                    }

                    break;

                case YGenericHub.YSTREAM_TCP:
                case YGenericHub.YSTREAM_TCP_CLOSE:
                    workingRequest = _workingRequests[tcpChanel].Peek();
                    if (workingRequest != null)
                    {
                        uint contentSize = messageReader.UnconsumedBufferLength;
                        //todo: try to copy data more efficently
                        byte[] data = new byte[contentSize];
                        if (contentSize > 0)
                        {
                            messageReader.ReadBytes(data);
                        }

                        workingRequest.imm_AppendResponseData(data);
                        if (ystream == YGenericHub.YSTREAM_TCP_CLOSE)
                        {
                            await Send_WSStream(sender, YGenericHub.YSTREAM_TCP_CLOSE, tcpChanel, null, 0);

                            _workingRequests[tcpChanel].Dequeue();
                            workingRequest.imm_close(YAPI.SUCCESS, "");
                        }
                    }

                    break;

                case YGenericHub.YSTREAM_META:
                    int  metatype = messageReader.ReadByte();
                    long nounce;
                    int  version;
                    switch (metatype)
                    {
                    case YGenericHub.USB_META_WS_ANNOUNCE:
                        version = messageReader.ReadByte();
                        if (version < YGenericHub.USB_META_WS_PROTO_V1 || messageSize < YGenericHub.USB_META_WS_ANNOUNCE_SIZE)
                        {
                            return;
                        }

                        _remoteVersion = version;
                        int maxtcpws = messageReader.ReadUInt16();         // ignore reserved word
                        if (maxtcpws > 0)
                        {
                            _tcpMaxWindowSize = maxtcpws;
                        }

                        nounce = messageReader.ReadUInt32();
                        byte[] serial_char = new byte[messageReader.UnconsumedBufferLength];
                        messageReader.ReadBytes(serial_char);
                        int len;
                        for (len = YAPI.YOCTO_BASE_SERIAL_LEN; len < serial_char.Length; len++)
                        {
                            if (serial_char[len] == 0)
                            {
                                break;
                            }
                        }

                        _remoteSerial   = YAPI.DefaultEncoding.GetString(serial_char, 0, len);
                        _remoteNouce    = nounce;
                        _connectionTime = YAPI.GetTickCount();
                        Random randomGenerator = new Random();
                        _nounce          = (uint)randomGenerator.Next();
                        _connectionState = ConnectionState.AUTHENTICATING;
                        await sendAuthenticationMeta(_webSock);

                        break;

                    case YGenericHub.USB_META_WS_AUTHENTICATION:
                        if (_connectionState != ConnectionState.AUTHENTICATING)
                        {
                            return;
                        }
                        version = messageReader.ReadByte();
                        if (version < YGenericHub.USB_META_WS_PROTO_V1 || messageSize < YGenericHub.USB_META_WS_AUTHENTICATION_SIZE)
                        {
                            return;
                        }

                        _tcpRoundTripTime = YAPI.GetTickCount() - _connectionTime + 1;
                        long uploadRate = _tcpMaxWindowSize * 1000 / (int)_tcpRoundTripTime;
                        _hub._yctx._Log(string.Format("WS:RTT={0}ms, WS={1}, uploadRate={2} KB/s\n", _tcpRoundTripTime, _tcpMaxWindowSize, uploadRate / 1000.0));
                        int flags = messageReader.ReadInt16();
                        messageReader.ReadUInt32();         // drop nounce
                        if ((flags & YGenericHub.USB_META_WS_AUTH_FLAGS_RW) != 0)
                        {
                            _rwAccess = true;
                        }
                        if ((flags & YGenericHub.USB_META_WS_VALID_SHA1) != 0)
                        {
                            byte[] remote_sha1 = new byte[20];
                            messageReader.ReadBytes(remote_sha1);
                            byte[] sha1 = imm_computeAUTH(_hub._http_params.User, _hub._http_params.Pass, _remoteSerial, _nounce);
                            if (remote_sha1.SequenceEqual(sha1))
                            {
                                _connectionState = ConnectionState.CONNECTED;
                            }
                            else
                            {
                                errorOnSession(YAPI.UNAUTHORIZED, string.Format("Authentication as {0} failed", _hub._http_params.User));
                            }
                        }
                        else
                        {
                            if (!_hub._http_params.imm_hasAuthParam())
                            {
                                _connectionState = ConnectionState.CONNECTED;
                            }
                            else
                            {
                                if (_hub._http_params.User == "admin" && !_rwAccess)
                                {
                                    errorOnSession(YAPI.UNAUTHORIZED, string.Format("Authentication as {0} failed", _hub._http_params.User));
                                }
                                else
                                {
                                    errorOnSession(YAPI.UNAUTHORIZED, string.Format("Authentication error : hub has no password for {0}", _hub._http_params.User));
                                }
                            }
                        }

                        break;

                    case YGenericHub.USB_META_WS_ERROR:
                        // drop reserved byte
                        messageReader.ReadByte();
                        int html_error = messageReader.ReadUInt16();
                        if (html_error == 401)
                        {
                            errorOnSession(YAPI.UNAUTHORIZED, "Authentication failed");
                        }
                        else
                        {
                            errorOnSession(YAPI.IO_ERROR, string.Format("Remote hub closed connection with error %d", html_error));
                        }

                        break;

                    case YGenericHub.USB_META_ACK_UPLOAD:
                        int tcpchan = messageReader.ReadByte();
                        workingRequest = _workingRequests[tcpchan].Peek();
                        if (workingRequest != null)
                        {
                            int   b0       = messageReader.ReadByte();
                            int   b1       = messageReader.ReadByte();
                            int   b2       = messageReader.ReadByte();
                            int   b3       = messageReader.ReadByte();
                            int   ackBytes = b0 + (b1 << 8) + (b2 << 16) + (b3 << 24);
                            ulong ackTime  = YAPI.GetTickCount();
                            if (_lastUploadAckTime[tcpchan] != 0 && ackBytes > _lastUploadAckBytes[tcpchan])
                            {
                                _lastUploadAckBytes[tcpchan] = ackBytes;
                                _lastUploadAckTime[tcpchan]  = ackTime;

                                int   deltaBytes = ackBytes - _lastUploadRateBytes[tcpchan];
                                ulong deltaTime  = ackTime - _lastUploadRateTime[tcpchan];
                                if (deltaTime < 500)
                                {
                                    break;         // wait more
                                }
                                if (deltaTime < 1000 && deltaBytes < 65536)
                                {
                                    break;         // wait more
                                }
                                _lastUploadRateBytes[tcpchan] = ackBytes;
                                _lastUploadRateTime[tcpchan]  = ackTime;
                                //fixme: workingRequest.reportProgress(ackBytes);
                                double newRate = deltaBytes * 1000.0 / deltaTime;
                                _uploadRate = (int)(0.8 * _uploadRate + 0.3 * newRate);          // +10% intentionally
                                _hub._yctx._Log(string.Format("Upload rate: {0:F2} KB/s (based on {1:F2} KB in {2:F}s)\n", newRate / 1000.0, deltaBytes / 1000.0, deltaTime / 1000.0));
                            }
                            else
                            {
                                _hub._yctx._Log("First Ack received\n");
                                _lastUploadAckBytes[tcpchan]  = ackBytes;
                                _lastUploadAckTime[tcpchan]   = ackTime;
                                _lastUploadRateBytes[tcpchan] = ackBytes;
                                _lastUploadRateTime[tcpchan]  = ackTime;
                                //fixme: workingRequest.reportProgress(ackBytes);
                            }
                        }

                        break;

                    default:
                        WSLOG(string.Format("unhandled Meta pkt {0}", ystream));
                        break;
                    }

                    break;

                default:
                    _hub._yctx._Log(string.Format("Invalid WS stream type ({0})\n", ystream));
                    break;
                }
            } catch (Exception ex) {
                _hub._yctx._Log(string.Format("Exception during WS message decoding :{0}\n", ex.Message));
                Debug.WriteLine(ex.ToString());
            }
        }
Beispiel #18
0
        internal async Task <byte[]> doRequestTask(string firstLine, byte[] rest_of_request, ulong mstimeout, object context, YGenericHub.RequestAsyncResult resultCallback, HandleIncommingData progressCb)
        {
            byte[] full_request;
            int    retrycount = 0;

            _context          = context;
            _resultCallback   = resultCallback;
            _requestTimeout   = mstimeout;
            _progressCallback = progressCb;

retry:
            if (retrycount++ == 2)
            {
                string msg = "too many retry";
                if (resultCallback != null)
                {
                    await resultCallback(context, null, YAPI.IO_ERROR, msg);

                    return(null);
                }
                else
                {
                    throw new YAPI_Exception(YAPI.IO_ERROR, msg);
                }
            }

            log(String.Format("Start({0}):{1}", _reuse_socket ? "reuse" : "new", firstLine));
            _startRequestTime = YAPI.GetTickCount();
            _header_found     = false;
            string persistent_tag = firstLine.Substring(firstLine.Length - 2);

            if (persistent_tag.Equals("&."))
            {
                firstLine += " \r\n";
            }
            else
            {
                firstLine += " \r\nConnection: close\r\n";
            }

            if (rest_of_request == null)
            {
                string str_request = firstLine + _hub.imm_getAuthorization(firstLine) + "\r\n";
                full_request = YAPI.DefaultEncoding.GetBytes(str_request);
            }
            else
            {
                string str_request = firstLine + _hub.imm_getAuthorization(firstLine);
                int    len         = str_request.Length;
                full_request = new byte[len + rest_of_request.Length];
                Array.Copy(YAPI.DefaultEncoding.GetBytes(str_request), 0, full_request, 0, len);
                Array.Copy(rest_of_request, 0, full_request, len, rest_of_request.Length);
            }

            _result.SetLength(0);
            _header.Length = 0;
            byte[]     buffer = new byte[1024];
            Task <int> readTask;

            try {
                if (!_reuse_socket)
                {
                    // Creates an unconnected socket
                    _socket = new StreamSocket();
                    // Connect the socket to the remote endpoint. Catch any errors.
                    HostName serverHost = new HostName(_hub._http_params.Host);
                    int      port       = _hub._http_params.Port;
                    _socket.Control.NoDelay = true;
                    string port_str = port.ToString();
                    await _socket.ConnectAsync(serverHost, port_str);

                    _out = _socket.OutputStream.AsStreamForWrite();
                    _in  = _socket.InputStream.AsStreamForRead();
                    log(String.Format(" - new socket ({0} / {1})", _socket.ToString(), _in.ToString()));
                }

                readTask = _in.ReadAsync(buffer, 0, buffer.Length);
                if (_reuse_socket)
                {
                    try {
                        Task task = await Task.WhenAny(readTask, Task.Delay(0));

                        if (task == readTask)
                        {
                            int read = readTask.Result;
                            if (read == 0)
                            {
                                //socket has been reseted
                                log(String.Format(" - reset socket {0} {1})", _socket.ToString(), read));
                                closeSocket();
                                goto retry;
                            }

                            string msg = "suspect data received before request. Reset the socket";
                            log(msg);
                            throw new Exception(msg);
                        }
                    } catch (Exception e) {
                        log("Reset socket connection:" + e.Message);
                        closeSocket();
                        goto retry;
                    }
                }
            } catch (Exception e) {
                log("Exception on socket connection:" + e.Message);
                closeSocket();
                if (resultCallback != null)
                {
                    await resultCallback(context, null, YAPI.IO_ERROR, e.Message);

                    return(null);
                }
                else
                {
                    throw new YAPI_Exception(YAPI.IO_ERROR, e.Message);
                }
            }

            // write request
            try {
                await _out.WriteAsync(full_request, 0, full_request.Length);

                await _out.FlushAsync();
            } catch (Exception e) {
                closeSocket();
                if (resultCallback != null)
                {
                    await resultCallback(context, null, YAPI.IO_ERROR, e.Message);

                    return(null);
                }
                else
                {
                    throw new YAPI_Exception(YAPI.IO_ERROR, e.Message);
                }
            }

            _reuse_socket = false;
            bool eof = false;

            do
            {
                int read;
                try {
                    int   waitms;
                    ulong now;
                    if (_requestTimeout > 0)
                    {
                        now = YAPI.GetTickCount();
                        ulong read_timeout = _startRequestTime + _requestTimeout;
                        if (read_timeout < now)
                        {
                            string msg = string.Format("Request took too long {0:D}ms", now - _startRequestTime);
                            if (resultCallback != null)
                            {
                                await resultCallback(context, null, YAPI.TIMEOUT, msg);

                                return(null);
                            }
                            else
                            {
                                throw new YAPI_Exception(YAPI.TIMEOUT, msg);
                            }
                        }

                        read_timeout -= now;
                        if (read_timeout > YIO_IDLE_TCP_TIMEOUT)
                        {
                            read_timeout = YIO_IDLE_TCP_TIMEOUT;
                        }

                        waitms = (int)read_timeout;
                    }
                    else
                    {
                        waitms = YIO_IDLE_TCP_TIMEOUT;
                    }

                    Task task = await Task.WhenAny(readTask, Task.Delay(waitms));

                    now = YAPI.GetTickCount();
                    if (task != readTask)
                    {
                        string msg = string.Format("Hub did not send data during {0:D}ms", waitms);
                        if (resultCallback != null)
                        {
                            await resultCallback(context, null, YAPI.IO_ERROR, msg);

                            return(null);
                        }
                        else
                        {
                            throw new YAPI_Exception(YAPI.TIMEOUT, msg);
                        }
                    }

                    read = readTask.Result;
                    log(String.Format("_requestProcesss read ={0:d} after{1}", read, now - _startRequestTime));
                } catch (IOException e) {
                    closeSocket();
                    if (resultCallback != null)
                    {
                        await resultCallback(context, null, YAPI.IO_ERROR, e.Message);

                        return(null);
                    }
                    else
                    {
                        throw new YAPI_Exception(YAPI.IO_ERROR, e.Message);
                    }
                }

                if (read <= 0)
                {
                    // end of connection
                    closeSocket();
                    log("end of connection " + _dbglabel);
                    eof = true;
                }
                else if (read > 0)
                {
                    if (imm_HandleIncommingData(buffer, read))
                    {
                        closeSocket();
                        goto retry;
                    }

                    if (_reuse_socket)
                    {
                        byte[] tmp = _result.ToArray();
                        if (tmp[tmp.Length - 1] == 10 && tmp[tmp.Length - 2] == 13)
                        {
                            log("end of request " + _dbglabel + " let the socket open for later");
                            eof = true;
                        }
                    }
                }

                if (!eof)
                {
                    readTask = _in.ReadAsync(buffer, 0, buffer.Length);
                }
            } while (!eof);

            if (_header.Length == 0 && _result.Length == 0)
            {
                log("Short request detected");
                closeSocket();
                goto retry;
            }


            byte[] final_res = _result.ToArray();
            if (resultCallback != null)
            {
                await resultCallback(context, final_res, YAPI.SUCCESS, "");
            }

            return(final_res);
        }
        internal override async Task <List <string> > firmwareUpdate(string serial, YFirmwareFile firmware, byte[] settings,
                                                                     UpdateProgress progress)
        {
            bool   use_self_flash = false;
            string baseurl        = "";
            bool   need_reboot    = true;

            if (_serial.StartsWith("VIRTHUB", StringComparison.Ordinal))
            {
                use_self_flash = false;
            }
            else if (serial.Equals(_serial))
            {
                use_self_flash = true;
            }
            else
            {
                // check if subdevice support self flashing
                try {
                    await _notificationHandler.hubRequestSync("GET /bySerial/" + serial + "/flash.json?a=state", null,
                                                              YIO_DEFAULT_TCP_TIMEOUT);

                    baseurl        = "/bySerial/" + serial;
                    use_self_flash = true;
                } catch (YAPI_Exception) { }
            }
            //5% -> 10%
            await progress(5, "Enter in bootloader");

            List <string> bootloaders = await getBootloaders();

            bool is_shield = serial.StartsWith("YHUBSHL1", StringComparison.Ordinal);

            foreach (string bl in bootloaders)
            {
                if (bl.Equals(serial))
                {
                    need_reboot = false;
                }
                else if (is_shield)
                {
                    if (bl.StartsWith("YHUBSHL1", StringComparison.Ordinal))
                    {
                        throw new YAPI_Exception(YAPI.IO_ERROR, "Only one YoctoHub-Shield is allowed in update mode");
                    }
                }
            }
            if (!use_self_flash && need_reboot && bootloaders.Count >= 4)
            {
                throw new YAPI_Exception(YAPI.IO_ERROR, "Too many devices in update mode");
            }
            // ensure flash engine is not busy
            byte[] bytes = await _notificationHandler.hubRequestSync("GET" + baseurl + "/flash.json?a=state", null,
                                                                     YIO_DEFAULT_TCP_TIMEOUT);

            string      uploadstate = YAPI.DefaultEncoding.GetString(bytes);
            YJSONObject uploadres   = new YJSONObject(uploadstate);

            uploadres.parse();
            string state = uploadres.getYJSONString("state").getString();

            if (state.Equals("uploading") || state.Equals("flashing"))
            {
                throw new YAPI_Exception(YAPI.IO_ERROR, "Cannot start firmware update: busy (" + state + ")");
            }
            // start firmware upload
            //10% -> 40%
            await progress(10, "Send firmware file");

            byte[] head_body = YDevice.imm_formatHTTPUpload("firmware", firmware.Data);
            await _notificationHandler.hubRequestSync("POST " + baseurl + "/upload.html", head_body, 0);

            //check firmware upload result
            bytes = await _notificationHandler.hubRequestSync("GET " + baseurl + "/flash.json?a=state", null,
                                                              YIO_10_MINUTES_TCP_TIMEOUT);

            string uploadresstr = YAPI.DefaultEncoding.GetString(bytes);

            uploadres = new YJSONObject(uploadresstr);
            uploadres.parse();
            state = uploadres.getString("state");
            if (state != "valid")
            {
                throw new YAPI_Exception(YAPI.IO_ERROR, "Upload of firmware failed: invalid firmware(" + state + ")");
            }
            if (uploadres.getInt("progress") != 100)
            {
                throw new YAPI_Exception(YAPI.IO_ERROR, "Upload of firmware failed: incomplete upload");
            }
            if (use_self_flash)
            {
                byte[]      startupConf;
                string      json       = YAPI.DefaultEncoding.GetString(settings);
                YJSONObject jsonObject = new YJSONObject(json);
                jsonObject.parse();
                YJSONObject settingsOnly = jsonObject.getYJSONObject("api");
                settingsOnly.remove("services");
                string startupConfStr = settingsOnly.ToString();
                startupConf = YAPI.DefaultEncoding.GetBytes(startupConfStr);
                await progress(20, "Upload startupConf.json");

                head_body = YDevice.imm_formatHTTPUpload("startupConf.json", startupConf);
                await _notificationHandler.hubRequestSync("POST " + baseurl + "/upload.html", head_body,
                                                          YIO_10_MINUTES_TCP_TIMEOUT);
                await progress(20, "Upload firmwareConf");

                head_body = YDevice.imm_formatHTTPUpload("firmwareConf", startupConf);
                await _notificationHandler.hubRequestSync("POST " + baseurl + "/upload.html", head_body,
                                                          YIO_10_MINUTES_TCP_TIMEOUT);
            }

            //40%-> 80%
            if (use_self_flash)
            {
                await progress(40, "Flash firmware");

                // the hub itself -> reboot in autoflash mode
                await _notificationHandler.hubRequestSync(
                    "GET " + baseurl + "/api/module/rebootCountdown?rebootCountdown=-1003", null, YIO_DEFAULT_TCP_TIMEOUT);

                await Task.Delay(TimeSpan.FromSeconds(7));
            }
            else
            {
                // reboot device to bootloader if needed
                if (need_reboot)
                {
                    // reboot subdevice
                    await _notificationHandler.hubRequestSync(
                        "GET /bySerial/" + serial + "/api/module/rebootCountdown?rebootCountdown=-2", null,
                        YIO_DEFAULT_TCP_TIMEOUT);
                }
                // verify that the device is in bootloader
                ulong  timeout = YAPI.GetTickCount() + YPROG_BOOTLOADER_TIMEOUT;
                byte[] res;
                bool   found = false;
                await progress(40, "Wait for device to be in bootloader");

                do
                {
                    List <string> list = await getBootloaders();

                    foreach (string bl in list)
                    {
                        if (bl.Equals(serial))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        //fixme: replace by async
                        //Thread.Sleep(100);
                    }
                } while (!found && YAPI.GetTickCount() < timeout);
                //start flash
                await progress(45, "Flash firmware");

                res = await _notificationHandler.hubRequestSync("GET /flash.json?a=flash&s=" + serial, null,
                                                                YIO_10_MINUTES_TCP_TIMEOUT);

                string      jsonstr  = YAPI.DefaultEncoding.GetString(res);
                YJSONObject flashres = new YJSONObject(jsonstr);
                flashres.parse();
                YJSONArray    logslist = flashres.getYJSONArray("logs");
                List <string> logs     = new List <string>(logslist.Length);
                for (int i = 0; i < logslist.Length; i++)
                {
                    logs.Add(logslist.getString(i));
                }
                return(logs);
            }

            return(null);
        }