/// <summary>
        /// Handles the ItemCommand event of the rLocations control.
        /// </summary>
        /// <param name="source">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterCommandEventArgs"/> instance containing the event data.</param>
        protected void rLocations_ItemCommand(object source, System.Web.UI.WebControls.RepeaterCommandEventArgs e)
        {
            int?locationId = (e.CommandArgument as string).AsIntegerOrNull();

            if (locationId.HasValue)
            {
                var rockContext = new RockContext();
                var location    = new LocationService(rockContext).Get(locationId.Value);
                if (location != null)
                {
                    if (e.CommandName == "Open" && !location.IsActive)
                    {
                        location.IsActive = true;
                        rockContext.SaveChanges();
                        KioskDevice.Clear();
                    }
                    else if (e.CommandName == "Close" && location.IsActive)
                    {
                        location.IsActive = false;
                        rockContext.SaveChanges();
                        KioskDevice.Clear();
                    }
                }

                BindManagerLocationsGrid();
            }
        }
        public void TestKioskPurchaseTicket()
        {
            FakeDeviceClient   fakeDeviceClient = new FakeDeviceClient();
            FakeEventScheduler fakeScheduler    = new FakeEventScheduler();

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Testing the purchase ticket simulated event..");

            KioskDevice device = new KioskDevice(deviceconfig, fakeDeviceClient, fakeScheduler);

            // execute a purchase ticket event and check the result, it should always be false
            Assert.IsFalse(fakeScheduler.EventList[0].EventDelegate());
            // that delegate should have sent one message to the cloud
            Assert.AreEqual(fakeDeviceClient.sendMessageLog.Count, 1);

            // check the message sent to make sure its correct
            // create a sample request for comparison
            PurchaseTicketRequest expectedRequest = new PurchaseTicketRequest()
            {
                DeviceId      = deviceconfig.DeviceId,
                DeviceType    = deviceconfig.DeviceType,
                TransactionId = "fakeId",
                CreateTime    = System.DateTime.UtcNow,
                Price         = 1,
                MethodName    = "ReceivePurchaseTicketResponse"
            };
            // get request message into an object so we can compare it
            PurchaseTicketRequest actualRequest = JsonConvert.DeserializeObject <PurchaseTicketRequest>(fakeDeviceClient.sendMessageLog[0]);

            // compare properties to make sure they're valid.
            Assert.AreEqual(actualRequest.DeviceId, expectedRequest.DeviceId);
            Assert.AreEqual(actualRequest.DeviceType, expectedRequest.DeviceType);
            Assert.AreEqual(actualRequest.MessageType, expectedRequest.MessageType);
            // skipping the TransactionID and CreationTime
            Assert.IsTrue(actualRequest.Price > 2);
            Assert.IsTrue(actualRequest.Price < 100);
            Assert.AreEqual(actualRequest.MethodName, expectedRequest.MethodName);

            //
            /// test the CloudToEvent PurchaseResponse call we expect back
            //
            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Testing the ticket approval direct method..");

            // test the direct method itself
            PurchaseTicketPayload approvePurchaseMethodkRequest = new PurchaseTicketPayload()
            {
                IsApproved  = true,
                DeviceId    = expectedRequest.DeviceId,
                DeviceType  = expectedRequest.DeviceType,
                MessageType = expectedRequest.MessageType,
            };
            string requestString = JsonConvert.SerializeObject(approvePurchaseMethodkRequest);
            // execute the method
            MethodRequest  methodRequest = new MethodRequest(expectedRequest.MethodName, Encoding.UTF8.GetBytes(requestString));
            MethodResponse methodresult  = fakeDeviceClient.directMethods[0](methodRequest, null).Result;

            // check results
            Assert.AreEqual(methodresult.Status, 200); // got back an ok
        }
        public void TestPurchaseDenied()
        {
            FakeDeviceClient   fakeDeviceClient = new FakeDeviceClient();
            FakeEventScheduler fakeScheduler    = new FakeEventScheduler();

            TestContext.WriteLine(">> Testing the Kiosk Device's Low Stock notification..");

            PurchaseTicketPayload approvePurchaseMethodkRequest = new PurchaseTicketPayload()
            {
                DeviceId    = deviceconfig.DeviceId,
                DeviceType  = deviceconfig.DeviceType,
                MessageType = "Purchase",
                IsApproved  = false
            };

            // create our test device
            KioskDevice device = new KioskDevice(deviceconfig, fakeDeviceClient, fakeScheduler);

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Purchasing tickets, shouldn't throw event");
            string        requestString = JsonConvert.SerializeObject(approvePurchaseMethodkRequest);
            MethodRequest methodRequest = new MethodRequest("ReceivePurchaseTicketResponse", Encoding.UTF8.GetBytes(requestString));


            MethodResponse myresult = fakeDeviceClient.directMethods[0](methodRequest, null).Result;

            // no ticket was issued, count remained the same and no message sent to cloud
            Assert.AreEqual(device.CurrentStockLevel, deviceconfig.InitialStockCount);
            Assert.AreEqual(fakeDeviceClient.sendMessageLog.Count, 0);
        }
Exemple #4
0
        /// <summary>
        /// Handles the ItemCommand event of the rLocations control.
        /// </summary>
        /// <param name="source">The source of the event.</param>
        /// <param name="e">The <see cref="System.Web.UI.WebControls.RepeaterCommandEventArgs"/> instance containing the event data.</param>
        protected void rLocations_ItemCommand(object source, System.Web.UI.WebControls.RepeaterCommandEventArgs e)
        {
            int?locationId = (e.CommandArgument as string).AsIntegerOrNull();

            if (locationId.HasValue)
            {
                var rockContext = new RockContext();
                var location    = new LocationService(rockContext).Get(locationId.Value);
                if (location != null)
                {
                    if (e.CommandName == "Open" && !location.IsActive)
                    {
                        location.IsActive = true;
                        rockContext.SaveChanges();
                    }
                    else if (e.CommandName == "Close" && location.IsActive)
                    {
                        location.IsActive = false;
                        rockContext.SaveChanges();
                    }

                    // flush the current kiosk ( the kiosk only caches groups, etc for active locations, so we need to flush anytime a location is opened/closed )
                    if (this.CurrentKioskId.HasValue)
                    {
                        KioskDevice.Flush(this.CurrentKioskId.Value);
                    }
                }

                BindManagerLocationsGrid();
            }
        }
        /// <summary>
        /// Handles the Click event of the btnSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
        protected void btnSave_Click(object sender, EventArgs e)
        {
            using (var rockContext = new RockContext())
            {
                GroupLocationService groupLocationService = new GroupLocationService(rockContext);
                ScheduleService      scheduleService      = new ScheduleService(rockContext);
                bool schedulesChanged = false;

                var gridViewRows = gGroupLocationSchedule.Rows;
                foreach (GridViewRow row in gridViewRows.OfType <GridViewRow>())
                {
                    int           groupLocationId = int.Parse(gGroupLocationSchedule.DataKeys[row.RowIndex].Value as string);
                    GroupLocation groupLocation   = groupLocationService.Get(groupLocationId);
                    if (groupLocation != null)
                    {
                        foreach (var fieldCell in row.Cells.OfType <DataControlFieldCell>())
                        {
                            var checkBoxTemplateField = fieldCell.ContainingField as CheckBoxEditableField;
                            if (checkBoxTemplateField != null)
                            {
                                CheckBox checkBox   = fieldCell.Controls[0] as CheckBox;
                                string   dataField  = (fieldCell.ContainingField as CheckBoxEditableField).DataField;
                                int      scheduleId = int.Parse(dataField.Replace("scheduleField_", string.Empty));

                                // update GroupLocationSchedule depending on if the Schedule is Checked or not
                                if (checkBox.Checked)
                                {
                                    // This schedule is selected, so if GroupLocationSchedule doesn't already have this schedule, add it
                                    if (!groupLocation.Schedules.Any(a => a.Id == scheduleId))
                                    {
                                        var schedule = scheduleService.Get(scheduleId);
                                        groupLocation.Schedules.Add(schedule);
                                        schedulesChanged = true;
                                    }
                                }
                                else
                                {
                                    // This schedule is not selected, so if GroupLocationSchedule has this schedule, delete it
                                    if (groupLocation.Schedules.Any(a => a.Id == scheduleId))
                                    {
                                        groupLocation.Schedules.Remove(groupLocation.Schedules.FirstOrDefault(a => a.Id == scheduleId));
                                        schedulesChanged = true;
                                    }
                                }
                            }
                        }
                    }
                }

                if (schedulesChanged)
                {
                    rockContext.SaveChanges();
                    KioskDevice.Clear();
                }
            }

            NavigateToHomePage();
        }
        /// <summary>
        /// Attempts to match a known kiosk based on the IP address of the client.
        /// </summary>
        private void GetKioskType(Kiosk kiosk, RockContext rockContext)
        {
            if (kiosk.KioskType != null)
            {
                DeviceService deviceService = new DeviceService(rockContext);
                //Load matching device and update or create information
                var device = deviceService.Queryable().Where(d => d.Name == kiosk.Name).FirstOrDefault();

                //create new device to match our kiosk
                if (device == null)
                {
                    device = new Device();
                    device.DeviceTypeValueId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.DEVICE_TYPE_CHECKIN_KIOSK).Id;
                    device.Name = kiosk.Name;
                    deviceService.Add(device);
                }

                device.LoadAttributes();
                device.IPAddress = kiosk.IPAddress;
                device.Locations.Clear();
                foreach (var loc in kiosk.KioskType.Locations.ToList())
                {
                    device.Locations.Add(loc);
                }
                device.PrintFrom       = kiosk.PrintFrom;
                device.PrintToOverride = kiosk.PrintToOverride;
                device.PrinterDeviceId = kiosk.PrinterDeviceId;
                rockContext.SaveChanges();

                if (PageParameter("DateTime").AsDateTime().HasValue)
                {
                    device.SetAttributeValue("core_device_DebugDateTime", PageParameter("datetime"));
                }
                else
                {
                    device.SetAttributeValue("core_device_DebugDateTime", "");
                }
                device.SaveAttributeValues(rockContext);

                CurrentKioskId      = device.Id;
                CurrentGroupTypeIds = kiosk.KioskType.GroupTypes.Select(gt => gt.Id).ToList();

                CurrentCheckinTypeId = kiosk.KioskType.CheckinTemplateId;

                CurrentCheckInState     = null;
                CurrentWorkflow         = null;
                Session["KioskTypeId"]  = kiosk.KioskType.Id;
                Session["KioskMessage"] = kiosk.KioskType.Message;
                KioskDevice.Remove(device.Id);
                SaveState();
                NavigateToNextPage();
            }
            else
            {
                ltDNS.Text      = kiosk.Name;
                pnlMain.Visible = true;
            }
        }
Exemple #7
0
        /// <summary>
        /// Printers the has cutter.
        /// </summary>
        /// <param name="labels">The labels.</param>
        /// <returns></returns>
        private static bool PrinterHasCutter(List <CheckInLabel> labels)
        {
            bool hasCutter = false;
            var  deviceId  = labels.Select(a => a.PrinterDeviceId).FirstOrDefault();

            if (deviceId != null)
            {
                KioskDevice kioskDevice = KioskDevice.Get(deviceId.GetValueOrDefault(), new List <int>());
                hasCutter = kioskDevice.Device.GetAttributeValue(Rock.SystemKey.DeviceAttributeKey.DEVICE_HAS_CUTTER).AsBoolean();
            }

            return(hasCutter);
        }
        public void TestBaseKioskDevice()
        {
            FakeDeviceClient   fakeDeviceClient = new FakeDeviceClient();
            FakeEventScheduler fakeScheduler    = new FakeEventScheduler();

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Testing the Kiosk Device's base functionality..");

            KioskDevice device = new KioskDevice(deviceconfig, fakeDeviceClient, fakeScheduler);

            // should only have 1 scheduled event
            Assert.AreEqual(fakeScheduler.EventList.Count, 1, "Incorrect number of scheduled events");

            // should only have 1 callback method
            Assert.AreEqual(fakeDeviceClient.directMethods.Count, 1, "Incorrect number of callback methods");
        }
Exemple #9
0
        private Kiosk ConfigureKiosk()
        {
            var rockContext = new RockContext();
            var kioskTypeId = ddlCampus.SelectedValue.AsInteger();

            var kioskType = KioskTypeCache.Get(kioskTypeId);

            var kioskName = "Mobile:" + kioskType.Name.RemoveAllNonAlphaNumericCharacters();

            var mobileUserCategory = CategoryCache.Get(org.secc.FamilyCheckin.Utilities.Constants.KIOSK_CATEGORY_MOBILEUSER);

            var kioskService = new KioskService(rockContext);
            var kiosk        = kioskService.Queryable("KioskType")
                               .Where(k => k.Name == kioskName)
                               .FirstOrDefault();

            if (kiosk == null)
            {
                kiosk = new Kiosk
                {
                    Name        = kioskName,
                    CategoryId  = mobileUserCategory.Id,
                    Description = "Automatically created mobile Kiosk"
                };
                kioskService.Add(kiosk);
            }

            kiosk.KioskTypeId = kioskType.Id;
            rockContext.SaveChanges();

            DeviceService deviceService = new DeviceService(rockContext);


            //Load matching device and update or create information
            var device = deviceService.Queryable("Location").Where(d => d.Name == kioskName).FirstOrDefault();

            var dirty = false;

            //create new device to match our kiosk
            if (device == null)
            {
                device = new Device();
                device.DeviceTypeValueId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.DEVICE_TYPE_CHECKIN_KIOSK).Id;
                device.Name = kioskName;
                deviceService.Add(device);
                device.PrintFrom       = PrintFrom.Client;
                device.PrintToOverride = PrintTo.Default;
                dirty = true;
            }

            var deviceLocationIds = device.Locations.Select(l => l.Id);
            var ktLocationIds     = kioskType.Locations.Select(l => l.Id);

            var unmatchedDeviceLocations = deviceLocationIds.Except(ktLocationIds).Any();
            var unmatchedKtLocations     = ktLocationIds.Except(deviceLocationIds).Any();

            if (unmatchedDeviceLocations || unmatchedKtLocations)
            {
                LocationService locationService = new LocationService(rockContext);
                device.Locations.Clear();
                foreach (var loc in kioskType.Locations.ToList())
                {
                    var location = locationService.Get(loc.Id);
                    device.Locations.Add(location);
                }
                dirty = true;
            }

            if (this.IsUserAuthorized(Rock.Security.Authorization.ADMINISTRATE))
            {
                device.LoadAttributes();

                if (PageParameter("datetime").IsNotNullOrWhiteSpace())
                {
                    device.SetAttributeValue("core_device_DebugDateTime", PageParameter("datetime"));
                }
                else
                {
                    device.SetAttributeValue("core_device_DebugDateTime", "");
                }
            }

            if (dirty)
            {
                rockContext.SaveChanges();
                device.SaveAttributeValues(rockContext);
                KioskDevice.Remove(device.Id);
            }

            LocalDeviceConfig.CurrentKioskId       = device.Id;
            LocalDeviceConfig.CurrentGroupTypeIds  = kiosk.KioskType.GroupTypes.Select(gt => gt.Id).ToList();
            LocalDeviceConfig.CurrentCheckinTypeId = kiosk.KioskType.CheckinTemplateId;

            CurrentCheckInState = null;
            CurrentWorkflow     = null;

            var kioskTypeCookie = this.Page.Request.Cookies["KioskTypeId"];

            if (kioskTypeCookie == null)
            {
                kioskTypeCookie = new System.Web.HttpCookie("KioskTypeId");
            }

            kioskTypeCookie.Expires = RockDateTime.Now.AddYears(1);
            kioskTypeCookie.Value   = kiosk.KioskType.Id.ToString();

            this.Page.Response.Cookies.Set(kioskTypeCookie);

            Session["KioskTypeId"] = kioskType.Id;
            SaveState();
            return(kiosk);
        }
        private void ActivateKiosk(Kiosk kiosk, bool logout)
        {
            RockContext   rockContext   = new RockContext();
            DeviceService deviceService = new DeviceService(rockContext);
            KioskService  kioskService  = new KioskService(rockContext);

            //The kiosk can come in a variety of states.
            //Get a fresh version with our context to avoid context errors.
            kiosk = kioskService.Get(kiosk.Id);

            //Load matching device and update or create information
            var device = deviceService.Queryable().Where(d => d.Name == kiosk.Name).FirstOrDefault();

            //create new device to match our kiosk
            if (device == null)
            {
                device = new Device();
                device.DeviceTypeValueId = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.DEVICE_TYPE_CHECKIN_KIOSK).Id;
                device.Name = kiosk.Name;
                deviceService.Add(device);
            }

            device.LoadAttributes();
            device.IPAddress = kiosk.IPAddress;
            device.Locations.Clear();
            foreach (var loc in kiosk.KioskType.Locations.ToList())
            {
                device.Locations.Add(loc);
            }
            device.PrintFrom       = kiosk.PrintFrom;
            device.PrintToOverride = kiosk.PrintToOverride;
            device.PrinterDeviceId = kiosk.PrinterDeviceId;
            rockContext.SaveChanges();

            if (PageParameter("DateTime").AsDateTime().HasValue)
            {
                device.SetAttributeValue("core_device_DebugDateTime", PageParameter("datetime"));
            }
            else
            {
                device.SetAttributeValue("core_device_DebugDateTime", "");
            }
            device.SaveAttributeValues(rockContext);

            LocalDeviceConfig.CurrentKioskId       = device.Id;
            LocalDeviceConfig.CurrentGroupTypeIds  = kiosk.KioskType.GroupTypes.Select(gt => gt.Id).ToList();
            LocalDeviceConfig.CurrentCheckinTypeId = kiosk.KioskType.CheckinTemplateId;

            CurrentCheckInState = null;
            CurrentWorkflow     = null;

            var kioskTypeCookie = this.Page.Request.Cookies["KioskTypeId"];

            if (kioskTypeCookie == null)
            {
                kioskTypeCookie = new System.Web.HttpCookie("KioskTypeId");
            }

            kioskTypeCookie.Expires = RockDateTime.Now.AddYears(1);
            kioskTypeCookie.Value   = kiosk.KioskType.Id.ToString();

            this.Page.Response.Cookies.Set(kioskTypeCookie);

            Session["KioskTypeId"]  = kiosk.KioskType.Id;
            Session["KioskMessage"] = kiosk.KioskType.Message;

            //Clean things up so we have the freshest possible version.
            KioskTypeCache.Remove(kiosk.KioskTypeId ?? 0);
            KioskDevice.Remove(device.Id);

            Dictionary <string, string> pageParameters = new Dictionary <string, string>();

            if (kiosk.KioskType.Theme.IsNotNullOrWhiteSpace() && !GetAttributeValue("Manual").AsBoolean())
            {
                LocalDeviceConfig.CurrentTheme = kiosk.KioskType.Theme;
                pageParameters.Add("theme", LocalDeviceConfig.CurrentTheme);
            }

            if (logout)
            {
                pageParameters.Add("logout", "true");
            }

            SaveState();

            NavigateToNextPage(pageParameters);
        }
Exemple #11
0
        public static void Clear(KioskDevice kioskDevice)
        {
            var groupTypeIds = kioskDevice.KioskGroupTypes.Select(gt => gt.GroupType.Id).ToList();

            Clear(groupTypeIds);
        }
Exemple #12
0
        public PrintSessionLabelsResponse PrintSessionLabels([FromUri] string session, [FromUri] int?kioskId = null)
        {
            List <Guid?> sessionGuids;

            //
            // The session data is a comma separated list of Guid values.
            //
            try
            {
                sessionGuids = session.SplitDelimitedValues()
                               .Select(a =>
                {
                    var guid = a.AsGuidOrNull();

                    //
                    // Check if this is a standard Guid format.
                    //
                    if (guid.HasValue)
                    {
                        return(guid.Value);
                    }

                    return(GuidHelper.FromShortStringOrNull(a));
                })
                               .ToList();
            }
            catch
            {
                sessionGuids = null;
            }

            //
            // If no session guids were found or an error occurred trying to
            // parse them, then return an error.
            //
            if (sessionGuids == null || sessionGuids.Count == 0)
            {
                return(new PrintSessionLabelsResponse
                {
                    Labels = new List <CheckInLabel>(),
                    Messages = new List <string>
                    {
                        "No check-in sessions were specified."
                    }
                });
            }

            using (var rockContext = new RockContext())
            {
                KioskDevice printer = null;

                //
                // If they specified a kiosk, attempt to load the printer for
                // that kiosk.
                //
                if (kioskId.HasValue && kioskId.Value != 0)
                {
                    var kiosk = KioskDevice.Get(kioskId.Value, null);
                    if ((kiosk?.Device?.PrinterDeviceId).HasValue)
                    {
                        //
                        // We aren't technically loading a kiosk, but this lets us
                        // load the printer IP address from cache rather than hitting
                        // the database each time.
                        //
                        printer = KioskDevice.Get(kiosk.Device.PrinterDeviceId.Value, null);
                    }
                }

                var attendanceService = new AttendanceService(rockContext);

                //
                // Retrieve all session label data from the database, then deserialize
                // it, then make one giant list of labels and finally order it.
                //
                var labels = attendanceService.Queryable()
                             .AsNoTracking()
                             .Where(a => sessionGuids.Contains(a.AttendanceCheckInSession.Guid))
                             .DistinctBy(a => a.AttendanceCheckInSessionId)
                             .Select(a => a.AttendanceData.LabelData)
                             .ToList()
                             .Select(a => a.FromJsonOrNull <List <CheckInLabel> >())
                             .Where(a => a != null)
                             .SelectMany(a => a)
                             .OrderBy(a => a.PersonId)
                             .ThenBy(a => a.Order)
                             .ToList();

                foreach (var label in labels)
                {
                    //
                    // If the label is being printed to the kiosk and client printing
                    // is enabled and the client has specified their device
                    // identifier then we need to update the label data to have the
                    // new printer information.
                    //
                    if (label.PrintTo == PrintTo.Kiosk && label.PrintFrom == PrintFrom.Client && printer != null)
                    {
                        label.PrinterDeviceId = printer.Device.Id;
                        label.PrinterAddress  = printer.Device.IPAddress;
                    }
                }

                var response = new PrintSessionLabelsResponse
                {
                    Labels = labels.Where(l => l.PrintFrom == PrintFrom.Client).ToList()
                };

                //
                // Update any labels to convert the relative URL path to an absolute URL
                // path for client printing.
                //
                if (response.Labels.Any())
                {
                    var urlRoot = Request.RequestUri.GetLeftPart(UriPartial.Authority);

                    if (Request.Headers.Contains("X-Forwarded-Proto") && Request.Headers.Contains("X-Forwarded-Host"))
                    {
                        urlRoot = $"{Request.Headers.GetValues( "X-Forwarded-Proto" ).First()}://{Request.Headers.GetValues( "X-Forwarded-Host" ).First()}";
                    }
#if DEBUG
                    // This is extremely useful when debugging with ngrok and an iPad on the local network.
                    // X-Original-Host will contain the name of your ngrok hostname, therefore the labels will
                    // get a LabelFile url that will actually work with that iPad.
                    if (Request.Headers.Contains("X-Forwarded-Proto") && Request.Headers.Contains("X-Original-Host"))
                    {
                        urlRoot = $"{Request.Headers.GetValues( "X-Forwarded-Proto" ).First()}://{Request.Headers.GetValues( "X-Original-Host" ).First()}";
                    }
#endif
                    response.Labels.ForEach(l => l.LabelFile = urlRoot + l.LabelFile);
                }

                var printFromServer = labels.Where(l => l.PrintFrom == PrintFrom.Server).ToList();

                if (printFromServer.Any())
                {
                    var messages = ZebraPrint.PrintLabels(printFromServer);
                    response.Messages = messages;
                }

                return(response);
            }
        }
        public void TestLowStock()
        {
            FakeDeviceClient   fakeDeviceClient = new FakeDeviceClient();
            FakeEventScheduler fakeScheduler    = new FakeEventScheduler();

            TestContext.WriteLine(">> Testing the Kiosk Device's Low Stock notification..");

            PurchaseTicketPayload approvePurchaseMethodkRequest = new PurchaseTicketPayload()
            {
                DeviceId    = deviceconfig.DeviceId,
                DeviceType  = deviceconfig.DeviceType,
                MessageType = MessageType.cmdPurchaseTicket,
                IsApproved  = true
            };

            // create our test device
            KioskDevice device = new KioskDevice(deviceconfig, fakeDeviceClient, fakeScheduler);

            device.InitializeAsync().Wait();

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Purchasing tickets, shouldn't throw event");
            string        requestString = JsonConvert.SerializeObject(approvePurchaseMethodkRequest);
            MethodRequest methodRequest = new MethodRequest("ReceivePurchaseTicketResponse", Encoding.UTF8.GetBytes(requestString));

            // fire events to bring the count down to right at threshold
            for (long count = this.deviceconfig.InitialStockCount; count > this.deviceconfig.LowStockThreshold; count--)
            {
                MethodResponse myresult = fakeDeviceClient.directMethods[0](methodRequest, null).Result;
                TestContext.WriteLine(">> Current stock level: " + device.CurrentStockLevel);
            }

            // now that we're at the threshold, lets clear all previous events
            fakeDeviceClient.sendMessageLog.Clear(); // clear out all messages to this point

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Purchasing 1 more ticket. Should send low stock notification");
            // purchase one more ticket.
            MethodResponse methodresult = fakeDeviceClient.directMethods[0](methodRequest, null).Result;

            // we expect 2 messages to have been sent
            Assert.AreEqual(fakeDeviceClient.sendMessageLog.Count, 2);
            // second message should make expected result
            LowStockRequest expectedRequest = new LowStockRequest()
            {
                DeviceId   = deviceconfig.DeviceId,
                DeviceType = deviceconfig.DeviceType,
                StockLevel = (deviceconfig.LowStockThreshold - 1),
            };
            // get actual message into an object so we can compare it
            LowStockRequest actualRequest = JsonConvert.DeserializeObject <LowStockRequest>(fakeDeviceClient.sendMessageLog[1]);

            TestContext.WriteLine(fakeDeviceClient.sendMessageLog[1]);
            // compare properties to make sure they're valid.
            Assert.AreEqual(actualRequest.DeviceId, expectedRequest.DeviceId);
            Assert.AreEqual(actualRequest.DeviceType, expectedRequest.DeviceType);
            Assert.AreEqual(actualRequest.MessageType, expectedRequest.MessageType);
            Assert.AreEqual(actualRequest.StockLevel, expectedRequest.StockLevel);

            TestContext.WriteLine(string.Empty);
            TestContext.WriteLine(">> Testing to make sure we don't have a second low stock warning ..");
            // make sure we don't throw the low stock warning again
            fakeDeviceClient.sendMessageLog.Clear(); // clear out all messages to this point
            // purchase one more ticket.
            methodresult = fakeDeviceClient.directMethods[0](methodRequest, null).Result;
            // we expect 2 messages to have been sent
            Assert.AreEqual(fakeDeviceClient.sendMessageLog.Count, 1);

            // reset the device to make sure ticket stock is reset using the desired property callback option
            device.SetDeviceStatusAsync(DeviceStatus.disabled).Wait(); // disable the device
            device.SetDeviceStatusAsync(DeviceStatus.enabled).Wait();  // enable the device
            // check the stock level
            Assert.AreEqual(deviceconfig.InitialStockCount, device.CurrentStockLevel, "Device stock levels were not reset back to initial after device rest");
        }