Library "autoplugins.brs" 'region Main '3-22-16 added script watchdog delay Sub Main() autorunVersion$ = "6.8.46" 'BA 3.8.0.41 customAutorunVersion$ = "6.8.416" 'storage delete feedpool zone message added, customer edit debugParams = EnableDebugging() sysFlags = CreateObject("roAssociativeArray") sysFlags.debugOn = debugParams.serialDebugOn sysFlags.systemLogDebugOn = debugParams.systemLogDebugOn modelObject = CreateObject("roDeviceInfo") sysInfo = CreateObject("roAssociativeArray") sysInfo.autorunVersion$ = autorunVersion$ sysInfo.customAutorunVersion$ = customAutorunVersion$ sysInfo.deviceUniqueID$ = modelObject.GetDeviceUniqueId() sysInfo.deviceFWVersion$ = modelObject.GetVersion() sysInfo.deviceModel$ = modelObject.GetModel() sysInfo.deviceFamily$ = modelObject.GetFamily() sysInfo.enableLogDeletion = true sysInfo.ipAddressWired$ = "Invalid" nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then currentConfig = nc.GetCurrentConfig() if type(currentConfig) = "roAssociativeArray" then if currentConfig.ip4_address <> "" then sysInfo.ipAddressWired$ = currentConfig.ip4_address endif endif endif nc = invalid sysInfo.modelSupportsWifi = false sysInfo.ipAddressWireless$ = "Invalid" nc = CreateObject("roNetworkConfiguration", 1) if type(nc) = "roNetworkConfiguration" then currentConfig = nc.GetCurrentConfig() if type(currentConfig) = "roAssociativeArray" then sysInfo.modelSupportsWifi = true if currentConfig.ip4_address <> "" then sysInfo.ipAddressWireless$ = currentConfig.ip4_address endif endif endif nc = invalid sysInfo.modelSupportsRoAudioOutput = false sysInfo.modelSupportsEnhancedSynchronization = false sysInfo.modelSupportsDiskMonitor = false if not (sysInfo.deviceFamily$ = "monaco" or sysInfo.deviceFamily$ = "pandora3" or sysInfo.deviceFamily$ = "apollo" or sysInfo.deviceFamily$ = "bpollo") then sysInfo.modelSupportsRoAudioOutput = true sysInfo.modelSupportsEnhancedSynchronization = true sysInfo.modelSupportsDiskMonitor = true videoMode = CreateObject("roVideoMode") edid = videoMode.GetEdidIdentity(true) UpdateEdidValues(edid, sysInfo) edid = invalid videoMode = invalid else UpdateEdidValues(invalid, sysInfo) endif ' check to see whether or not the current firmware meets the minimum compatibility requirements versionNumber% = modelObject.GetVersionNumber() if sysInfo.deviceFamily$ = "panther" then minVersionNumber% = 264082 minVersion$ = "4.7.146" else if sysInfo.deviceFamily$ = "cheetah" then minVersionNumber% = 264082 minVersion$ = "4.7.146" else if sysInfo.deviceFamily$ = "puma" then minVersionNumber% = 264082 minVersion$ = "4.7.146" else minVersionNumber% = 199435 minVersion$ = "3.11.11" endif if versionNumber% < minVersionNumber% then videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode = invalid r=CreateObject("roRectangle",0,resY/2-resY/64,resX,resY/32) twParams = CreateObject("roAssociativeArray") twParams.LineCount = 1 twParams.TextMode = 2 twParams.Rotation = 0 twParams.Alignment = 1 tw=CreateObject("roTextWidget",r,1,2,twParams) tw.PushString("Firmware needs to be upgraded to " + minVersion$ + " or greater") tw.Show() sleep(120000) RebootSystem() endif ' determine if the storage device is writable tmpFileName$ = "bs~69-96.txt" WriteAsciiFile(tmpFileName$, "1") readValue$ = ReadAsciiFile(tmpFileName$) if len(readValue$) = 1 and readValue$ = "1" then sysInfo.storageIsWriteProtected = false DeleteFile(tmpFileName$) else sysInfo.storageIsWriteProtected = true endif diagnosticCodes = newDiagnosticCodes() RunBsp(sysFlags, sysInfo, diagnosticCodes) End Sub Sub UpdateEdidValues(edid As Object, sysInfo As Object) if type(edid) = "roAssociativeArray" then sysInfo.edidMonitorSerialNumber$ = edid.serial_number_string sysInfo.edidYearOfManufacture$ = edid.year_of_manufacture sysInfo.edidMonitorName$ = edid.monitor_name sysInfo.edidManufacturer$ = edid.manufacturer sysInfo.edidUnspecifiedText$ = edid.text_string sysInfo.edidSerialNumber$ = StripLeadingSpaces(stri(edid.serial_number)) sysInfo.edidManufacturerProductCode$ = edid.product sysInfo.edidWeekOfManufacture$ = edid.week_of_manufacture else sysInfo.edidMonitorSerialNumber$ = "" sysInfo.edidYearOfManufacture$ = "" sysInfo.edidMonitorName$ = "" sysInfo.edidManufacturer$ = "" sysInfo.edidUnspecifiedText$ = "" sysInfo.edidSerialNumber$ = "" sysInfo.edidManufacturerProductCode$ = "" sysInfo.edidWeekOfManufacture$ = "" endif End Sub Sub DisplayErrorScreen(msg1$ As String, msg2$ As String, msToWaitBeforeRebooting As Integer) videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode = invalid r = CreateObject("roRectangle", 0, 0, resX, resY) twParams = CreateObject("roAssociativeArray") twParams.LineCount = 1 twParams.TextMode = 2 twParams.Rotation = 0 twParams.Alignment = 1 tw=CreateObject("roTextWidget",r,1,2,twParams) tw.PushString("") tw.Show() r=CreateObject("roRectangle",0,resY/2-resY/32,resX,resY/32) tw=CreateObject("roTextWidget",r,1,2,twParams) tw.PushString(msg1$) tw.Show() r2=CreateObject("roRectangle",0,resY/2,resX,resY/32) tw2=CreateObject("roTextWidget",r2,1,2,twParams) tw2.PushString(msg2$) tw2.Show() globalAA = GetGlobalAA() globalAA.bsp.msgPort.SetWatchdogTimeout(0) msgPort = CreateObject("roMessagePort") msg = wait(msToWaitBeforeRebooting, msgPort) RebootSystem() End Sub Sub DisplayStorageDeviceLockedMessage() DisplayErrorScreen("The attached storage device is write protected.", "Remove it, enable writing, and reboot the device.", 0) End Sub Sub DisplayIncompatibleModelMessage(publishedModel$ As String, currentModel$ As String) DisplayErrorScreen("This presentation targets a BrightSign " + publishedModel$ + " and won't play on a " + currentModel$ + ".", "Remove the card, publish a compatible presentation, and reboot the device.", 120000) End Sub Function EnableDebugging() As Object debugParams = CreateObject("roAssociativeArray") debugParams.serialDebugOn = false debugParams.systemLogDebugOn = false syncSpec = CreateObject("roSyncSpec") if syncSpec.ReadFromFile("current-sync.xml") or syncSpec.ReadFromFile("local-sync.xml") or syncSpec.ReadFromFile("localToBSN-sync.xml") then if syncSpec.LookupMetadata("client", "enableSerialDebugging") = "True" then debugParams.serialDebugOn = true endif if syncSpec.LookupMetadata("client", "enableSystemLogDebugging") = "True" then debugParams.systemLogDebugOn = true endif syncSpec = invalid endif return debugParams End Function Sub WriteRegistrySetting(key$ As String, value$ As String) m.registrySection.Write(key$, value$) End Sub Function GetRegistrySettingValue(newRegistryKey$ As String, oldRegistryKey$ As String) As String value$ = m.registrySection.Read(newRegistryKey$) if value$ = "" then value$ = m.registrySection.Read(oldRegistryKey$) endif return value$ End Function Sub ReadCachedRegistrySettings() m.registrySettings = CreateObject("roAssociativeArray") m.registrySettings.lwsConfig$ = m.registrySection.Read("nlws") m.registrySettings.lwsUserName$ = m.registrySection.Read("nlwsu") m.registrySettings.lwsPassword$ = m.registrySection.Read("nlwsp") m.registrySettings.lwsEnableUpdateNotifications$ = m.registrySection.Read("nlwseun") if m.registrySettings.lwsEnableUpdateNotifications$ = "" then m.registrySettings.lwsEnableUpdateNotifications$ = "yes" endif m.registrySettings.unitName$ = m.GetRegistrySettingValue("un", "unitName") m.registrySettings.unitNamingMethod$ = m.GetRegistrySettingValue("unm", "unitNamingMethod") m.registrySettings.unitDescription$ = m.GetRegistrySettingValue("ud", "unitDescription") m.registrySettings.playbackLoggingEnabled = m.registrySection.Read("ple") m.registrySettings.eventLoggingEnabled = m.registrySection.Read("ele") m.registrySettings.diagnosticLoggingEnabled = m.registrySection.Read("dle") m.registrySettings.stateLoggingEnabled = m.registrySection.Read("sle") m.registrySettings.uploadLogFilesAtBoot = m.registrySection.Read("uab") m.registrySettings.uploadLogFilesAtSpecificTime = m.registrySection.Read("uat") m.registrySettings.uploadLogFilesTime$ = m.registrySection.Read("ut") m.registrySettings.OnlyDownloadIfCached$ = m.registrySection.Read("OnlyDownloadIfCached") m.registrySettings.timeBetweenNetConnects$ = m.GetRegistrySettingValue("tbnc", "timeBetweenNetConnects") m.registrySettings.contentDownloadsRestricted = m.GetRegistrySettingValue("cdr", "contentDownloadsRestricted") m.registrySettings.contentDownloadRangeStart = m.GetRegistrySettingValue("cdrs", "contentDownloadRangeStart") m.registrySettings.contentDownloadRangeLength = m.GetRegistrySettingValue("cdrl", "contentDownloadRangeLength") m.registrySettings.timeBetweenHeartbeats$ = m.GetRegistrySettingValue("tbh", "tbh") m.registrySettings.heartbeatsRestricted = m.GetRegistrySettingValue("hr", "hr") m.registrySettings.heartbeatsRangeStart = m.GetRegistrySettingValue("hrs", "hrs") m.registrySettings.heartbeatsRangeLength = m.GetRegistrySettingValue("hdrl", "hrl") m.registrySettings.rateLimitModeOutsideWindow$ = m.registrySection.Read("rlmow") m.registrySettings.rateLimitRateOutsideWindow$ = m.registrySection.Read("rlrow") m.registrySettings.rateLimitModeInWindow$ = m.registrySection.Read("rlmiw") m.registrySettings.rateLimitRateInWindow$ = m.registrySection.Read("rlriw") m.registrySettings.rateLimitModeInitialDownloads$ = m.registrySection.Read("rlmid") m.registrySettings.rateLimitRateInitialDownloads$ = m.registrySection.Read("rlrid") m.registrySettings.rateLimitModeOutsideWindow_2$ = m.registrySection.Read("rlmow2") if m.registrySettings.rateLimitModeOutsideWindow_2$ = "" then m.registrySettings.rateLimitModeOutsideWindow_2$ = "default" m.registrySettings.rateLimitRateOutsideWindow_2$ = m.registrySection.Read("rlrow2") if m.registrySettings.rateLimitRateOutsideWindow_2$ = "" then m.registrySettings.rateLimitRateOutsideWindow_2$ = "0" m.registrySettings.rateLimitModeInWindow_2$ = m.registrySection.Read("rlmiw2") if m.registrySettings.rateLimitModeInWindow_2$ = "" then m.registrySettings.rateLimitModeInWindow_2$ = "default" m.registrySettings.rateLimitRateInWindow_2$ = m.registrySection.Read("rlriw2") if m.registrySettings.rateLimitRateInWindow_2$ = "" then m.registrySettings.rateLimitRateInWindow_2$ = "0" m.registrySettings.rateLimitModeInitialDownloads_2$ = m.registrySection.Read("rlmid2") if m.registrySettings.rateLimitModeInitialDownloads_2$ = "" then m.registrySettings.rateLimitModeInitialDownloads_2$ = "default" m.registrySettings.rateLimitRateInitialDownloads_2$ = m.registrySection.Read("rlrid2") if m.registrySettings.rateLimitRateInitialDownloads_2$ = "" then m.registrySettings.rateLimitRateInitialDownloads_2$ = "0" ' wired parameters m.registrySettings.networkConnectionPriorityWired$ = m.registrySection.Read("ncp") if m.registrySettings.networkConnectionPriorityWired$ = "" then m.registrySettings.networkConnectionPriorityWired$ = "0" ' wireless parameters m.registrySettings.networkConnectionPriorityWireless$ = m.registrySection.Read("ncp2") if m.registrySettings.networkConnectionPriorityWireless$ = "" then m.registrySettings.networkConnectionPriorityWireless$ = "0" m.registrySettings.tbnco$ = m.registrySection.Read("tbnco") m.registrySettings.useProxy = m.registrySection.Read("up") if m.registrySettings.useProxy = "yes" then m.registrySettings.proxy$ = m.registrySection.Read("ps") else m.registrySettings.proxy$ = "" endif m.registrySettings.useWireless$ = m.registrySection.Read("wifi") m.registrySettings.ssid$ = m.registrySection.Read("ss") m.registrySettings.passphrase$ = m.registrySection.Read("pp") m.registrySettings.timeServer$ = m.GetRegistrySettingValue("ts", "timeServer") m.registrySettings.wiredNetworkingParameters = {} m.registrySettings.wiredNetworkingParameters.networkConnectionPriority$ = m.registrySection.Read("ncp") m.registrySettings.wirelessNetworkingParameters = {} m.registrySettings.wirelessNetworkingParameters.networkConnectionPriority$ = m.registrySection.Read("ncp2") if m.registrySettings.useWireless$ = "yes" then m.registrySettings.wirelessNetworkingParameters.useDHCP$ = m.registrySection.Read("dhcp") m.registrySettings.wirelessNetworkingParameters.staticIPAddress$ = m.registrySection.Read("sip") m.registrySettings.wirelessNetworkingParameters.subnetMask$ = m.registrySection.Read("sm") m.registrySettings.wirelessNetworkingParameters.gateway$ = m.registrySection.Read("gw") m.registrySettings.wirelessNetworkingParameters.dns1$ = m.registrySection.Read("d1") m.registrySettings.wirelessNetworkingParameters.dns2$ = m.registrySection.Read("d2") m.registrySettings.wirelessNetworkingParameters.dns3$ = m.registrySection.Read("d3") m.registrySettings.wiredNetworkingParameters.useDHCP$ = m.registrySection.Read("dhcp2") m.registrySettings.wiredNetworkingParameters.staticIPAddress$ = m.registrySection.Read("sip2") m.registrySettings.wiredNetworkingParameters.subnetMask$ = m.registrySection.Read("sm2") m.registrySettings.wiredNetworkingParameters.gateway$ = m.registrySection.Read("gw2") m.registrySettings.wiredNetworkingParameters.dns1$ = m.registrySection.Read("d12") m.registrySettings.wiredNetworkingParameters.dns2$ = m.registrySection.Read("d22") m.registrySettings.wiredNetworkingParameters.dns3$ = m.registrySection.Read("d32") else m.registrySettings.wiredNetworkingParameters.useDHCP$ = m.registrySection.Read("dhcp") m.registrySettings.wiredNetworkingParameters.staticIPAddress$ = m.registrySection.Read("sip") m.registrySettings.wiredNetworkingParameters.subnetMask$ = m.registrySection.Read("sm") m.registrySettings.wiredNetworkingParameters.gateway$ = m.registrySection.Read("gw") m.registrySettings.wiredNetworkingParameters.dns1$ = m.registrySection.Read("d1") m.registrySettings.wiredNetworkingParameters.dns2$ = m.registrySection.Read("d2") m.registrySettings.wiredNetworkingParameters.dns3$ = m.registrySection.Read("d3") endif m.registrySettings.contentXfersEnabledWired$ = m.registrySection.Read("cwr") m.registrySettings.textFeedsXfersEnabledWired$ = m.registrySection.Read("twr") m.registrySettings.healthXfersEnabledWired$ = m.registrySection.Read("hwr") m.registrySettings.mediaFeedsXfersEnabledWired$ = m.registrySection.Read("mwr") m.registrySettings.logUploadsXfersEnabledWired$ = m.registrySection.Read("lwr") m.registrySettings.contentXfersEnabledWireless$ = m.registrySection.Read("cwf") m.registrySettings.textFeedsXfersEnabledWireless$ = m.registrySection.Read("twf") m.registrySettings.healthXfersEnabledWireless$ = m.registrySection.Read("hwf") m.registrySettings.mediaFeedsXfersEnabledWireless$ = m.registrySection.Read("mwf") m.registrySettings.logUploadsXfersEnabledWireless$ = m.registrySection.Read("lwf") m.registrySettings.logDate$ = m.registrySection.Read("ld") m.registrySettings.logCounter$ = m.registrySection.Read("lc") m.registrySettings.dwsEnabled$ = m.registrySection.Read("dwse") m.registrySettings.dwsPassword$ = m.registrySection.Read("dwsp") m.registrySettings.idleScreenColor$ = m.registrySection.Read("isc") if m.registrySettings.idleScreenColor$ = "" then m.registrySettings.idleScreenColor$ = "FF000000" endif m.registrySettings.usbContentUpdatePassword$ = m.registrySection.Read("uup") End Sub Sub RunBsp(sysFlags As Object, sysInfo As Object, diagnosticCodes As Object) msgPort = CreateObject("roMessagePort") msgPort.SetWatchdogTimeout(60) BSP = newBSP(sysFlags, msgPort) BSP.GetRegistrySettingValue = GetRegistrySettingValue BSP.ReadCachedRegistrySettings = ReadCachedRegistrySettings BSP.WriteRegistrySetting = WriteRegistrySetting BSP.ReadCachedRegistrySettings() BSP.globalVariables = NewGlobalVariables() BSP.controlPort = CreateObject("roControlPort", "BrightSign") BSP.controlPort.SetPort(msgPort) BSP.controlPortIdentity = stri(BSP.controlPort.GetIdentity()) BSP.sh = CreateObject("roStorageHotplug") BSP.sh.SetPort(msgPort) BSP.nh = CreateObject("roNetworkHotplug") BSP.nh.SetPort(msgPort) if sysInfo.modelSupportsDiskMonitor then BSP.diskMonitor = CreateObject("roDiskMonitor") BSP.diskMonitor.SetPort(msgPort) endif BSP.videoMode = CreateObject("roVideoMode") BSP.videoMode.SetPort(msgPort) ' create objects for lighting controllers BSP.blcs = CreateObject("roArray", 3, true) BSP.blcs[0] = CreateObject("roControlPort", "LightController-0-CONTROL") BSP.blcs[1] = CreateObject("roControlPort", "LightController-1-CONTROL") BSP.blcs[2] = CreateObject("roControlPort", "LightController-2-CONTROL") ' create objects for blc diagnostics BSP.blcDiagnostics = CreateObject("roArray", 3, true) BSP.blcDiagnostics[0] = CreateObject("roControlPort", "LightController-0-DIAGNOSTICS") if type(BSP.blcDiagnostics[0]) = "roControlPort" then BSP.blcDiagnostics[0].SetPort(msgPort) endif BSP.blcDiagnostics[1] = CreateObject("roControlPort", "LightController-1-DIAGNOSTICS") if type(BSP.blcDiagnostics[1]) = "roControlPort" then BSP.blcDiagnostics[1].SetPort(msgPort) endif BSP.blcDiagnostics[2] = CreateObject("roControlPort", "LightController-2-DIAGNOSTICS") if type(BSP.blcDiagnostics[2]) = "roControlPort" then BSP.blcDiagnostics[2].SetPort(msgPort) endif ' create objects for all attached button panels BSP.bpInputPorts = CreateObject("roArray", 3, true) BSP.bpInputPortIdentities = CreateObject("roArray", 3, true) BSP.bpInputPortHardware = CreateObject("roArray", 3, true) BSP.bpInputPortConfigurations = CreateObject("roArray", 3, true) BSP.bpInputPorts[0] = CreateObject("roControlPort", "TouchBoard-0-GPIO") if type(BSP.bpInputPorts[0]) = "roControlPort" then BSP.bpInputPortIdentities[0] = stri(BSP.bpInputPorts[0].GetIdentity()) BSP.bpInputPorts[0].SetPort(msgPort) properties = BSP.bpInputPorts[0].GetProperties() BSP.bpInputPortHardware[0] = properties.hardware BSP.bpInputPortConfigurations[0] = 0 endif BSP.bpInputPorts[1] = CreateObject("roControlPort", "TouchBoard-1-GPIO") if type(BSP.bpInputPorts[1]) = "roControlPort" then BSP.bpInputPortIdentities[1] = stri(BSP.bpInputPorts[1].GetIdentity()) BSP.bpInputPorts[1].SetPort(msgPort) properties = BSP.bpInputPorts[1].GetProperties() BSP.bpInputPortHardware[1] = properties.hardware BSP.bpInputPortConfigurations[1] = 0 endif BSP.bpInputPorts[2] = CreateObject("roControlPort", "TouchBoard-2-GPIO") if type(BSP.bpInputPorts[2]) = "roControlPort" then BSP.bpInputPortIdentities[2] = stri(BSP.bpInputPorts[2].GetIdentity()) BSP.bpInputPorts[2].SetPort(msgPort) properties = BSP.bpInputPorts[2].GetProperties() BSP.bpInputPortHardware[2] = properties.hardware BSP.bpInputPortConfigurations[2] = 0 endif BSP.sysInfo = sysInfo BSP.diagnosticCodes = diagnosticCodes BSP.diagnostics.SetSystemInfo(sysInfo, diagnosticCodes) BSP.logging.SetSystemInfo(sysInfo, diagnosticCodes) ' if the device is configured for local file networking with content transfers, require that the storage is writable if BSP.registrySettings.lwsConfig$ = "c" and BSP.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() if BSP.registrySettings.lwsConfig$ = "c" or BSP.registrySettings.lwsConfig$ = "s" then lwsUserName$ = BSP.registrySettings.lwsUserName$ lwsPassword$ = BSP.registrySettings.lwsPassword$ if (len(lwsUserName$) + len(lwsPassword$)) > 0 then credentials = CreateObject("roAssociativeArray") credentials.AddReplace(lwsUserName$, lwsPassword$) else credentials = invalid end if BSP.localServer = CreateObject("roHttpServer", { port: 8080 }) BSP.localServer.SetPort(msgPort) BSP.GetIDAA = { HandleEvent: GetID, mVar: BSP } BSP.GetUDPEventsAA = { HandleEvent: GetUDPEvents, mVar: BSP } BSP.GetRemoteDataAA = { HandleEvent: GetRemoteData, mVar: BSP } BSP.GetUserVarsAA = { HandleEvent: GetUserVars, mVar: BSP } BSP.GetCurrentStatusAA = { HandleEvent: GetCurrentStatus, mVar: BSP } BSP.GetIDInfoPageAA = { HandleEvent: GetIDInfoPage, mVar: BSP } BSP.FilePostedAA = { HandleEvent: FilePosted, mVar: BSP } BSP.SyncSpecPostedAA = { HandleEvent: SyncSpecPosted, mVar: BSP } BSP.PrepareForTransferAA = { HandleEvent: PrepareForTransfer, mVar: BSP } BSP.SpecifyCardSizeLimitsAA = { HandleEvent: SpecifyCardSizeLimits, mVar: BSP } BSP.SendUdpRestAA = { HandleEvent: SendUdpRest, mVar: BSP } BSP.GetUserVariableCategoriesAA = { HandleEvent: GetUserVariableCategories, mVar: BSP } BSP.GetUserVariablesByCategoryAA = { HandleEvent: GetUserVariablesByCategory, mVar: BSP } BSP.localServer.AddGetFromFile({ url_path: "/GetAutorun", content_type: "text/plain; charset=utf-8", filename: "autorun.brs"}) BSP.localServer.AddGetFromEvent({ url_path: "/GetID", user_data: BSP.GetIDAA }) BSP.localServer.AddGetFromEvent({ url_path: "/GetUDPEvents", user_data: BSP.GetUDPEventsAA }) BSP.localServer.AddGetFromEvent({ url_path: "/GetRemoteData", user_data: BSP.GetRemoteDataAA }) BSP.localServer.AddGetFromEvent({ url_path: "/GetUserVars", user_data: BSP.GetUserVarsAA}) BSP.localServer.AddGetFromEvent({ url_path: "/GetCurrentStatus", user_data: BSP.GetCurrentStatusAA, passwords: credentials }) BSP.localServer.AddGetFromEvent({ url_path: "/", user_data: BSP.GetIDInfoPageAA}) BSP.localServer.AddGetFromEvent({ url_path: "/GetUserVariableCategories", user_data: BSP.GetUserVariableCategoriesAA}) BSP.localServer.AddGetFromEvent({ url_path: "/GetUserVariablesByCategory", user_data: BSP.GetUserVariablesByCategoryAA}) BSP.localServer.AddPostToFile({ url_path: "/UploadFile", destination_directory: GetDefaultDrive(), user_data: BSP.FilePostedAA, passwords: credentials }) BSP.localServer.AddPostToFile({ url_path: "/UploadSyncSpec", destination_directory: GetDefaultDrive(), user_data: BSP.SyncSpecPostedAA, passwords: credentials }) BSP.localServer.AddPostToFile({ url_path: "/PrepareForTransfer", destination_directory: GetDefaultDrive(), user_data: BSP.PrepareForTransferAA, passwords: credentials }) BSP.localServer.AddGetFromEvent({ url_path: "/SpecifyCardSizeLimits", user_data: BSP.SpecifyCardSizeLimitsAA, passwords: credentials}) BSP.localServer.AddPostToFormData({ url_path: "/SendUDP", user_data: BSP.SendUdpRestAA }) unitName$ = BSP.registrySettings.unitName$ unitNamingMethod$ = BSP.registrySettings.unitNamingMethod$ unitDescription$ = BSP.registrySettings.unitDescription$ if BSP.registrySettings.lwsConfig$ = "c" then BSP.lwsConfig$ = "content" else BSP.lwsConfig$ = "status" endif service = { name: "BrightSign Web Service", type: "_http._tcp", port: 8080, _functionality: BSP.lwsConfig$, _serialNumber: sysInfo.deviceUniqueID$, _unitName: unitName$, _unitNamingMethod: unitNamingMethod$, _unitDescription: unitDescription$ } BSP.advert = CreateObject("roNetworkAdvertisement", service) if BSP.advert = invalid then stop end if else BSP.lwsConfig$ = "none" endif ' create will fail if storage device is formatted as NTSF ok = CreateDirectory("pool") ok = CreateDirectory("feedPool") ok = CreateDirectory("feed_cache") BSP.assetPool = CreateObject("roAssetPool", "pool") BSP.feedPool = CreateObject("roAssetPool", "feedPool") activeSyncSpec = invalid localCurrentSync = CreateObject("roSyncSpec") if localCurrentSync.ReadFromFile("local-sync.xml") then activeSyncSpec = localCurrentSync BSP.assetCollection = localCurrentSync.GetAssets("download") BSP.assetPoolFiles = CreateObject("roAssetPoolFiles", BSP.assetPool, BSP.assetCollection) ' update registry setting for USB content updates if necessary metadata = localCurrentSync.GetMetadata("client") if metadata.DoesExist("usbUpdatePassword") then usbUpdatePassphrase$ = localCurrentSync.LookupMetadata("client", "usbUpdatePassword") if usbUpdatePassphrase$ <> BSP.registrySettings.usbContentUpdatePassword$ then BSP.registrySettings.usbContentUpdatePassword$ = usbUpdatePassphrase$ BSP.WriteRegistrySetting("uup", usbUpdatePassphrase$) endif endif endif ' networking is considered active if current-sync.xml is present. networkedCurrentSyncValid = false networkedCurrentSync = CreateObject("roSyncSpec") if networkedCurrentSync.ReadFromFile("current-sync.xml") or networkedCurrentSync.ReadFromFile("localToBSN-sync.xml") then activeSyncSpec = networkedCurrentSync ' if the device is configured for networking, require that the storage is writable if BSP.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() networkedCurrentSyncValid = true BSP.contentXfersEnabledWired = GetDataTransferEnabled(networkedCurrentSync, "contentXfersEnabledWired") BSP.textFeedsXfersEnabledWired = GetDataTransferEnabled(networkedCurrentSync, "textFeedsXfersEnabledWired") BSP.healthXfersEnabledWired = GetDataTransferEnabled(networkedCurrentSync, "healthXfersEnabledWired") BSP.mediaFeedsXfersEnabledWired = GetDataTransferEnabled(networkedCurrentSync, "mediaFeedsXfersEnabledWired") BSP.logUploadsXfersEnabledWired = GetDataTransferEnabled(networkedCurrentSync, "logUploadsXfersEnabledWired") BSP.contentXfersEnabledWireless = GetDataTransferEnabled(networkedCurrentSync, "contentXfersEnabledWireless") BSP.textFeedsXfersEnabledWireless = GetDataTransferEnabled(networkedCurrentSync, "textFeedsXfersEnabledWireless") BSP.healthXfersEnabledWireless = GetDataTransferEnabled(networkedCurrentSync, "healthXfersEnabledWireless") BSP.mediaFeedsXfersEnabledWireless = GetDataTransferEnabled(networkedCurrentSync, "mediaFeedsXfersEnabledWireless") BSP.logUploadsXfersEnabledWireless = GetDataTransferEnabled(networkedCurrentSync, "logUploadsXfersEnabledWireless") BSP.networkingHSM = newNetworkingStateMachine(BSP, BSP.msgPort) BSP.networkingHSM.SetSystemInfo(sysInfo, diagnosticCodes) BSP.logging.networking = BSP.networkingHSM BSP.assetCollection = networkedCurrentSync.GetAssets("download") BSP.assetPoolFiles = CreateObject("roAssetPoolFiles", BSP.assetPool, BSP.assetCollection) BSP.downloadFiles = networkedCurrentSync.GetFileList("download") BSP.networkingActive = true BSP.networkingHSM.Initialize() else BSP.networkingHSM = invalid BSP.networkingActive = false endif ' determine and set file paths for global files globalAA = GetGlobalAA() globalAA.autoscheduleFilePath$ = GetPoolFilePath(BSP.assetPoolFiles, "autoschedule.xml") globalAA.resourcesFilePath$ = GetPoolFilePath(BSP.assetPoolFiles, "resources.txt") globalAA.boseProductsFilePath$ = GetPoolFilePath(BSP.assetPoolFiles, "BoseProducts.xml") if globalAA.autoscheduleFilePath$ = "" then stop ' initialize logging parameters playbackLoggingEnabled = false eventLoggingEnabled = false diagnosticLoggingEnabled = false stateLoggingEnabled = false uploadLogFilesAtBoot = false uploadLogFilesAtSpecificTime = false uploadLogFilesTime% = 0 if networkedCurrentSyncValid then if networkedCurrentSync.LookupMetadata("client", "playbackLoggingEnabled") = "yes" then playbackLoggingEnabled = true if networkedCurrentSync.LookupMetadata("client", "eventLoggingEnabled") = "yes" then eventLoggingEnabled = true if networkedCurrentSync.LookupMetadata("client", "diagnosticLoggingEnabled") = "yes" then diagnosticLoggingEnabled = true if networkedCurrentSync.LookupMetadata("client", "stateLoggingEnabled") = "yes" then stateLoggingEnabled = true if networkedCurrentSync.LookupMetadata("client", "uploadLogFilesAtBoot") = "yes" then uploadLogFilesAtBoot = true if networkedCurrentSync.LookupMetadata("client", "uploadLogFilesAtSpecificTime") = "yes" then uploadLogFilesAtSpecificTime = true uploadLogFilesTime$ = networkedCurrentSync.LookupMetadata("client", "uploadLogFilesTime") ' as of BrightAuthor 3.0, logging parameters are set when the user performs a standalone publish - no uploads for a standalone unit, so leave those values false else if type(localCurrentSync) = "roSyncSpec" then if localCurrentSync.LookupMetadata("client", "playbackLoggingEnabled") = "yes" then playbackLoggingEnabled = true else if localCurrentSync.LookupMetadata("client", "playbackLoggingEnabled") = "no" then playbackLoggingEnabled = false else if BSP.registrySettings.playbackLoggingEnabled = "yes" then playbackLoggingEnabled = true endif if localCurrentSync.LookupMetadata("client", "eventLoggingEnabled") = "yes" then eventLoggingEnabled = true else if localCurrentSync.LookupMetadata("client", "eventLoggingEnabled") = "no" then eventLoggingEnabled = false else if BSP.registrySettings.eventLoggingEnabled = "yes" then eventLoggingEnabled = true endif if localCurrentSync.LookupMetadata("client", "diagnosticLoggingEnabled") = "yes" then diagnosticLoggingEnabled = true else if localCurrentSync.LookupMetadata("client", "diagnosticLoggingEnabled") = "no" then diagnosticLoggingEnabled = false else if BSP.registrySettings.diagnosticLoggingEnabled = "yes" then diagnosticLoggingEnabled = true endif if localCurrentSync.LookupMetadata("client", "stateLoggingEnabled") = "yes" then stateLoggingEnabled = true else if localCurrentSync.LookupMetadata("client", "stateLoggingEnabled") = "no" then stateLoggingEnabled = false else if BSP.registrySettings.stateLoggingEnabled = "yes" then stateLoggingEnabled = true endif uploadLogFilesTime$ = "" BSP.GetDataTransferEnabledFromRegistry = GetDataTransferEnabledFromRegistry BSP.contentXfersEnabledWired = BSP.GetDataTransferEnabledFromRegistry("contentXfersEnabledWired$") BSP.textFeedsXfersEnabledWired = BSP.GetDataTransferEnabledFromRegistry("textFeedsXfersEnabledWired$") BSP.healthXfersEnabledWired = BSP.GetDataTransferEnabledFromRegistry("healthXfersEnabledWired$") BSP.mediaFeedsXfersEnabledWired = BSP.GetDataTransferEnabledFromRegistry("mediaFeedsXfersEnabledWired$") BSP.logUploadsXfersEnabledWired = BSP.GetDataTransferEnabledFromRegistry("logUploadsXfersEnabledWired$") BSP.contentXfersEnabledWireless = BSP.GetDataTransferEnabledFromRegistry("contentXfersEnabledWireless$") BSP.textFeedsXfersEnabledWireless = BSP.GetDataTransferEnabledFromRegistry("textFeedsXfersEnabledWireless$") BSP.healthXfersEnabledWireless = BSP.GetDataTransferEnabledFromRegistry("healthXfersEnabledWireless$") BSP.mediaFeedsXfersEnabledWireless = BSP.GetDataTransferEnabledFromRegistry("mediaFeedsXfersEnabledWireless$") BSP.logUploadsXfersEnabledWireless = BSP.GetDataTransferEnabledFromRegistry("logUploadsXfersEnabledWireless$") else if BSP.registrySettings.playbackLoggingEnabled = "yes" then playbackLoggingEnabled = true if BSP.registrySettings.eventLoggingEnabled = "yes" then eventLoggingEnabled = true if BSP.registrySettings.stateLoggingEnabled = "yes" then stateLoggingEnabled = true if BSP.registrySettings.diagnosticLoggingEnabled = "yes" then diagnosticLoggingEnabled = true if BSP.registrySettings.uploadLogFilesAtBoot = "yes" then uploadLogFilesAtBoot = true if BSP.registrySettings.uploadLogFilesAtSpecificTime = "yes" then uploadLogFilesAtSpecificTime = true uploadLogFilesTime$ = BSP.registrySettings.uploadLogFilesTime$ endif if uploadLogFilesTime$ <> "" then uploadLogFilesTime% = int(val(uploadLogFilesTime$)) ' if the device is configured for logging, require that the storage is writable if (playbackLoggingEnabled or eventLoggingEnabled or stateLoggingEnabled or diagnosticLoggingEnabled) and BSP.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() ' setup logging BSP.logging.InitializeLogging(playbackLoggingEnabled, eventLoggingEnabled, stateLoggingEnabled, diagnosticLoggingEnabled, uploadLogFilesAtBoot, uploadLogFilesAtSpecificTime, uploadLogFilesTime%) BSP.logging.WriteDiagnosticLogEntry(diagnosticCodes.EVENT_STARTUP, BSP.sysInfo.deviceFWVersion$ + chr(9) + BSP.sysInfo.autorunVersion$ + chr(9) + BSP.sysInfo.customAutorunVersion$) BSP.InitializeNonPrintableKeyboardCodeList() ' ensure roAssetPool objects were created properly if type(BSP.assetPool) <> "roAssetPool" then BSP.diagnostics.PrintDebug("Unable to create roAssetPool for directory pool.") BSP.logging.WriteDiagnosticLogEntry(BSP.diagnosticCodes.EVENT_UNABLE_TO_CREATE_ASSET_POOL, "pool") BSP.logging.FlushLogFile() endif if type(BSP.feedPool) <> "roAssetPool" then BSP.diagnostics.PrintDebug("Unable to create roAssetPool for directory feedPool.") BSP.logging.WriteDiagnosticLogEntry(BSP.diagnosticCodes.EVENT_UNABLE_TO_CREATE_ASSET_POOL, "feedPool") BSP.logging.FlushLogFile() endif ' protect assets if not BSP.assetPool.ProtectAssets( "current", BSP.assetCollection ) then BSP.logging.WriteDiagnosticLogEntry(BSP.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, BSP.assetPool.GetFailureReason()) BSP.logging.FlushLogFile() BSP.diagnostics.PrintDebug("### ProtectFiles failed: " + BSP.assetPool.GetFailureReason()) stop endif ' limit pool sizes BSP.limitStorageSpace = false BSP.SetPoolSizes( activeSyncSpec ) ' Read and parse BoseProducts.xml BSP.boseProductSpecs = ReadBoseProductsFile() ' Get tuner data BSP.scannedChannels = GetScannedChannels() BSP.tunerScanPercentageComplete$ = "0" ' GPIO state machines and associated data structures dim gpioStateMachineRequired[8] dim gpioSM[8] BSP.gpioStateMachineRequired = gpioStateMachineRequired BSP.gpioSM = gpioSM ' BP state machines and associated data structures dim bpStateMachineRequired[3,11] dim bpInputUsed[3,11] dim bpOutputUsed[3,11] dim bpSM[3,11] BSP.bpStateMachineRequired = bpStateMachineRequired BSP.bpInputUsed = bpInputUsed BSP.bpOutputUsed = bpOutputUsed BSP.bpSM = bpSM 'custom 6-17-15 Clean lca BSP.sdcl = clean_Initialize(msgport, BSP) BSP.defwatchdog = false ' Create state machines ' Player state machine BSP.playerHSM = newPlayerStateMachine(BSP) BSP.playerHSM.SetSystemInfo(sysInfo, diagnosticCodes) ' Zone state machines are created by the Player state machine when it parses the schedule and autoplay files BSP.playerHSM.Initialize() BSP.CheckBLCsStatus() BSP.EventLoop() End Sub Sub SetPoolSizes( syncSpec As Object ) As Object limitStorageSpace$ = syncSpec.LookupMetadata("client", "limitStorageSpace") if lcase(limitStorageSpace$) = "true" then limitStorageSpace = true spaceLimitedByAbsoluteSize = syncSpec.LookupMetadata("client", "spaceLimitedByAbsoluteSize") publishedDataSizeLimitMB = syncSpec.LookupMetadata("client", "publishedDataSizeLimitMB") publishedDataSizeLimitPercentage = syncSpec.LookupMetadata("client", "publishedDataSizeLimitPercentage") dynamicDataSizeLimitMB = syncSpec.LookupMetadata("client", "dynamicDataSizeLimitMB") dynamicDataSizeLimitPercentage = syncSpec.LookupMetadata("client", "dynamicDataSizeLimitPercentage") htmlDataSizeLimitPercentage = syncSpec.LookupMetadata("client", "htmlDataSizeLimitPercentage") htmlDataSizeLimitMB = syncSpec.LookupMetadata("client", "htmlDataSizeLimitMB") else limitStorageSpace = false endif if limitStorageSpace then if lcase(spaceLimitedByAbsoluteSize) <> "true" then ' convert from percentage settings to absolute settings du = CreateObject("roStorageInfo", "./") totalCardSizeMB% = du.GetSizeInMegabytes() ' pool size for published data publishedDataSizeLimitPercentage% = int(val(publishedDataSizeLimitPercentage)) maximumPublishedDataPoolSizeMB% = publishedDataSizeLimitPercentage% * totalCardSizeMB% / 100 ' pool size for dynamic data dynamicDataSizeLimitPercentage% = int(val(dynamicDataSizeLimitPercentage)) maximumDynamicDataPoolSizeMB% = dynamicDataSizeLimitPercentage% * totalCardSizeMB% / 100 ' size for html data htmlDataSizeLimitPercentage% = int(val(htmlDataSizeLimitPercentage)) maximumHTMLDataPoolSizeMB% = htmlDataSizeLimitPercentage% * totalCardSizeMB% / 100 else maximumPublishedDataPoolSizeMB% = int(val(publishedDataSizeLimitMB)) maximumDynamicDataPoolSizeMB% = int(val(dynamicDataSizeLimitMB)) maximumHTMLDataPoolSizeMB% = int(val(htmlDataSizeLimitMB)) endif ok = m.assetPool.SetMaximumPoolSizeMegabytes( maximumPublishedDataPoolSizeMB% ) ' if not ok ?? ok = m.feedPool.SetMaximumPoolSizeMegabytes( maximumDynamicDataPoolSizeMB% ) ' if not ok ?? if maximumHTMLDataPoolSizeMB% > 0 then ' max int is 2147483647 => <2048 MB if maximumHTMLDataPoolSizeMB% >= 2048 then maximumHTMLDataPoolSizeMB% = 2047 endif r=CreateObject("roRectangle",0,0,0,0) htmlWidget = CreateObject("roHtmlWidget", r) if type(htmlWidget) = "roHtmlWidget" then ok = htmlWidget.SetAppCacheSize( maximumHTMLDataPoolSizeMB% * 1024.0 * 1024.0 ) ok = htmlWidget.SetAppCacheDir( "" ) htmlWidget = invalid endif endif else ' clear prior settings ok = m.assetPool.SetMaximumPoolSizeMegabytes( -1 ) ' if not ok ?? ok = m.feedPool.SetMaximumPoolSizeMegabytes( -1 ) ' if not ok ?? endif End Sub Sub CheckBLCsStatus() CheckBLCStatus(m.blcs[0], 0) CheckBLCStatus(m.blcs[1], 0) CheckBLCStatus(m.blcs[2], 0) End Sub Sub CheckBLCStatus(controlPort As Object, channel% As Integer) if type(controlPort) <> "roControlPort" return control_cmd = CreateObject("roArray", 4, false) CHANNEL_CMD_STATUS% = &h1700 control_cmd[ 0 ] = CHANNEL_CMD_STATUS% control_cmd[ 1 ] = channel% ' Channel to check status for (note use 0 for main power) control_cmd[ 2 ] = 0 ' unused control_cmd[ 3 ] = 0 ' unused controlPort.SetOutputValues(control_cmd) End Sub Function GetDataTransferEnabled(syncSpec As Object, syncSpecEntry$ As String) As Boolean spec$ = syncSpec.LookupMetadata("client", syncSpecEntry$) dataTransferEnabled = true if lcase(spec$) = "false" then dataTransferEnabled = false return dataTransferEnabled End Function Function GetDataTransferEnabledFromRegistry(registryKey$ As String) As Boolean if m.registrySettings.Lookup(registryKey$) = "False" then registryValue = false else registryValue = true endif return registryValue End Function Function GetBinding(wiredTransferEnabled As Boolean, wirelessTransferEnabled As Boolean) As Integer binding% = -1 if wiredTransferEnabled <> wirelessTransferEnabled then if wiredTransferEnabled then binding% = 0 else binding% = 1 endif endif return binding% End Function Function newBSP(sysFlags As Object, msgPort As Object) As Object BSP = {} globalAA = GetGlobalAA() globalAA.bsp = BSP BSP.msgPort = msgPort registrySection = CreateObject("roRegistrySection", "networking") if type(registrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop BSP.registrySection = registrySection BSP.systemTime = CreateObject("roSystemTime") BSP.diagnostics = newDiagnostics(sysFlags) BSP.Restart = Restart BSP.StartPlayback = StartPlayback BSP.ClearImageBuffers = ClearImageBuffers BSP.newLogging = newLogging BSP.logging = BSP.newLogging() BSP.LogActivePresentation = LogActivePresentation BSP.SetTouchRegions = SetTouchRegions BSP.InitializeTouchScreen = InitializeTouchScreen BSP.AddRectangularTouchRegion = AddRectangularTouchRegion BSP.TuneToChannel = TuneToChannel BSP.SendTuneFailureMessage = SendTuneFailureMessage BSP.ExecuteMediaStateCommands = ExecuteMediaStateCommands BSP.ExecuteTransitionCommands = ExecuteTransitionCommands BSP.ExecuteCmd = ExecuteCmd BSP.ExecuteSwitchPresentationCommand = ExecuteSwitchPresentationCommand BSP.ChangeRFChannel = ChangeRFChannel BSP.EventLoop = EventLoop BSP.MapDigitalOutput = MapDigitalOutput BSP.SetAudioOutput = SetAudioOutput BSP.SetAudioMode = SetAudioMode BSP.MapStereoOutput = MapStereoOutput BSP.SetSpdifMute = SetSpdifMute BSP.SetAnalogMute = SetAnalogMute BSP.SetHDMIMute = SetHDMIMute BSP.UnmuteAllAudio = UnmuteAllAudio BSP.UnmuteAudioConnector = UnmuteAudioConnector BSP.SetAllAudioOutputs = SetAllAudioOutputs BSP.SetAudioMode1 = SetAudioMode1 BSP.MuteAudioOutput = MuteAudioOutput BSP.MuteAudioOutputs = MuteAudioOutputs BSP.SetConnectorVolume = SetConnectorVolume BSP.ChangeConnectorVolume = ChangeConnectorVolume BSP.SetZoneVolume = SetZoneVolume BSP.ChangeZoneVolume = ChangeZoneVolume BSP.SetZoneChannelVolume = SetZoneChannelVolume BSP.ChangeZoneChannelVolume = ChangeZoneChannelVolume BSP.SetVideoVolume = SetVideoVolume BSP.SetVideoVolumeByConnector = SetVideoVolumeByConnector BSP.ChangeVideoVolumeByConnector = ChangeVideoVolumeByConnector BSP.IncrementVideoVolumeByConnector = IncrementVideoVolumeByConnector BSP.DecrementVideoVolumeByConnector = DecrementVideoVolumeByConnector BSP.ChangeVideoVolume = ChangeVideoVolume BSP.IncrementVideoVolume = IncrementVideoVolume BSP.DecrementVideoVolume = DecrementVideoVolume BSP.SetVideoChannnelVolume = SetVideoChannnelVolume BSP.IncrementVideoChannnelVolumes = IncrementVideoChannnelVolumes BSP.DecrementVideoChannnelVolumes = DecrementVideoChannnelVolumes BSP.SetAudioVolume = SetAudioVolume BSP.SetAudioVolumeByConnector = SetAudioVolumeByConnector BSP.ChangeAudioVolumeByConnector = ChangeAudioVolumeByConnector BSP.IncrementAudioVolumeByConnector = IncrementAudioVolumeByConnector BSP.DecrementAudioVolumeByConnector = DecrementAudioVolumeByConnector BSP.ChangeAudioVolume = ChangeAudioVolume BSP.IncrementAudioVolume = IncrementAudioVolume BSP.DecrementAudioVolume = DecrementAudioVolume BSP.SetAudioChannnelVolume = SetAudioChannnelVolume BSP.IncrementAudioChannelVolumes = IncrementAudioChannelVolumes BSP.DecrementAudioChannelVolumes = DecrementAudioChannelVolumes BSP.SetAudioVolumeLimits = SetAudioVolumeLimits BSP.GetZone = GetZone BSP.GetVideoZone = GetVideoZone BSP.ChangeChannelVolumes = ChangeChannelVolumes BSP.SetChannelVolumes = SetChannelVolumes BSP.PauseVideo = PauseVideo BSP.ResumeVideo = ResumeVideo BSP.SetPowerSaveMode = SetPowerSaveMode BSP.CecDisplayOn = CecDisplayOn BSP.CecDisplayOff = CecDisplayOff BSP.CecPhilipsSetVolume = CecPhilipsSetVolume BSP.SendCecCommand = SendCecCommand BSP.WaitForSyncResponse = WaitForSyncResponse BSP.XMLAutoschedule = XMLAutoSchedule BSP.GetNonPrintableKeyboardCode = GetNonPrintableKeyboardCode BSP.InitializeNonPrintableKeyboardCodeList = InitializeNonPrintableKeyboardCodeList BSP.ConfigureBPs = ConfigureBPs BSP.ConfigureBP = ConfigureBP BSP.ConfigureBPButton = ConfigureBPButton BSP.ConfigureBPInput = ConfigureBPInput BSP.ConfigureGPIOButton = ConfigureGPIOButton BSP.ConfigureGPIOInput = ConfigureGPIOInput BSP.GetID = GetID BSP.GetCurrentStatus = GetCurrentStatus BSP.GetUDPEvents = GetUDPEvents BSP.GetRemoteData = GetRemoteData BSP.GetIDInfoPage = GetIDInfoPage BSP.FilePosted = FilePosted BSP.SyncSpecPosted = SyncSpecPosted BSP.PrepareForTransfer = PrepareForTransfer BSP.FreeSpaceOnDrive = FreeSpaceOnDrive BSP.GetBoseProductSpec = GetBoseProductSpec BSP.CreateSerial = CreateSerial BSP.CreateUDPSender = CreateUDPSender BSP.SendUDPNotification = SendUDPNotification BSP.udpNotificationAddress$ = "224.0.200.200" BSP.udpNotificationPort% = 5000 BSP.rssFileIndex% = 0 BSP.GetRSSTempFilename = GetRSSTempFilename BSP.ReadVariablesDB = ReadVariablesDB BSP.ReadVariables = ReadVariables BSP.ReadSchema1Tables = ReadSchema1Tables BSP.CreateSchema2Tables = CreateSchema2Tables BSP.CreateDBTable = CreateDBTable BSP.DeleteDBTable = DeleteDBTable BSP.DropSchema1Tables = DropSchema1Tables BSP.AddDBSection = AddDBSection BSP.AddDBCategory = AddDBCategory BSP.AddDBVariable = AddDBVariable BSP.DeleteDBVariable = DeleteDBVariable BSP.UpdateDBVariable = UpdateDBVariable BSP.UpdateDBVariableDefaultValue = UpdateDBVariableDefaultValue BSP.UpdateDBVariableMediaUrl = UpdateDBVariableMediaUrl BSP.UpdateDBVariablePosition = UpdateDBVariablePosition BSP.GetDBVersion = GetDBVersion BSP.SetDBVersion = SetDBVersion BSP.UpdateDBVersion = UpdateDBVersion BSP.GetDBSectionNames = GetDBSectionNames BSP.GetDBSectionId = GetDBSectionId BSP.GetDBCategoryId = GetDBCategoryId BSP.GetDBCategoryNames = GetDBCategoryNames BSP.DoGetCategories = DoGetCategories BSP.GetOrderedVariables = GetOrderedVariables BSP.GetUserVariableCategoryList = GetUserVariableCategoryList BSP.GetUserVariablesByCategoryList = GetUserVariablesByCategoryList BSP.GetUserVariablesGivenCategory = GetUserVariablesGivenCategory BSP.GetCategoryIdFromAccess = GetCategoryIdFromAccess BSP.GetCategoryFromSection = GetCategoryFromSection BSP.ExecuteDBInsert = ExecuteDBInsert BSP.ExecuteDBSelect = ExecuteDBSelect BSP.GetDBVersionCallback = GetDBVersionCallback BSP.GetDBCategoryIdCallback = GetDBCategoryIdCallback BSP.GetDBSectionNamesCallback = GetDBSectionNamesCallback BSP.GetDBSectionIdCallback = GetDBSectionIdCallback BSP.ReadSchema1TablesCallback = ReadSchema1TablesCallback BSP.GetUserVariablesGivenCategoryCallback = GetUserVariablesGivenCategoryCallback BSP.ReadVariablesCallback = ReadVariablesCallback BSP.ExportVariablesDBToAsciiFile = ExportVariablesDBToAsciiFile BSP.GetUserVariable = GetUserVariable BSP.DeleteVariable = DeleteVariable BSP.ResetVariables = ResetVariables BSP.ResetVariable = ResetVariable BSP.ChangeUserVariableValue = ChangeUserVariableValue BSP.UpdateDataFeed = UpdateDataFeed BSP.CreateUserVariablesFromDataFeed = CreateUserVariablesFromDataFeed BSP.CheckBLCsStatus = CheckBLCsStatus BSP.CheckBLCStatus = CheckBLCStatus BSP.UpdateIPAddressUserVariables = UpdateIPAddressUserVariables BSP.UpdateRFChannelCountUserVariables = UpdateRFChannelCountUserVariables BSP.UpdateRFChannelUserVariables = UpdateRFChannelUserVariables BSP.UpdateTunerScanPercentageCompleteUserVariables = UpdateTunerScanPercentageCompleteUserVariables BSP.UpdateEdidUserVariables = UpdateEdidUserVariables BSP.GetAttachedFiles = GetAttachedFiles BSP.PostponeRestart = PostponeRestart BSP.ProcessMediaEndEvent = ProcessMediaEndEvent BSP.SetPoolSizes = SetPoolSizes return BSP End Function 'endregion 'region Local Web server Sub GetConfigurationPage(userData as Object, e as Object) mVar = userData.mVar ' print "respond to GetConfigurationPage request" e.AddResponseHeader("Content-type", "text/html; charset=utf-8") if type(mVar.sign) = "roAssociativeArray" and mVar.sign.deviceWebPageDisplay$ = "None" then e.SetResponseBodyString("") if not e.SendResponse(403) then stop else if mVar.deviceWebPageFilePath$ <> "" webPageContents$ = ReadAsciiFile(mVar.deviceWebPageFilePath$) e.SetResponseBodyString(webPageContents$) if not e.SendResponse(200) then stop else e.SetResponseBodyString("") if not e.SendResponse(404) then stop endif End Sub Sub SendUdpRest(userData as Object, e as Object) mVar = userData.mVar args = e.GetFormData() CreateUDPSender(mVar) for each key in args value=args[key] mVar.udpSender.Send(value) '' print "sendUDP key: "+key+" value: "+value next if not e.SendResponse(200) then stop end Sub Function GetCategoryFromSection(sectionName$ as String, categoryName$ as String) As Integer sectionId% = m.GetDBSectionId(sectionName$) if sectionId% < 0 return -1 return m.GetDBCategoryId(sectionId%, categoryName$) End Function Sub SetValuesByCategory(userData as Object, e as Object) mVar = userData.mVar args = e.GetFormData() if args.DoesExist("category") then categoryName$ = args.Lookup("category") categoryId% = mVar.GetCategoryFromSection(mVar.activePresentation$, categoryName$) if categoryId% < 0 then categoryId% = mVar.GetCategoryFromSection("Shared", categoryName$) endif if categoryId% > 0 then ' is there a way to check that userVariableName exists in the db? for each userVariableName in args mVar.UpdateDBVariable(categoryId%, userVariableName, args.Lookup(userVariableName)) next endif endif e.AddResponseHeader("Location", e.GetRequestHeader("Referer")) if not e.SendResponse(302) then stop End Sub Sub SetValues(userData as Object, e as Object) ' print "respond to SetValues request" mVar = userData.mVar args = e.GetFormData() ' print args userVariables = mVar.currentUserVariables userVariablesUpdated = false if type(userVariables) = "roAssociativeArray" then for each userVariableName in args if userVariables.DoesExist(userVariableName) then userVariable = userVariables.Lookup(userVariableName) userVariable.SetCurrentValue(args.Lookup(userVariableName), false) userVariablesUpdated = true endif next endif e.AddResponseHeader("Location", e.GetRequestHeader("Referer")) if not e.SendResponse(302) then stop if userVariablesUpdated then userVariablesChanged = CreateObject("roAssociativeArray") userVariablesChanged["EventType"] = "USER_VARIABLES_UPDATED" mVar.msgPort.PostMessage(userVariablesChanged) ' Notify controlling devices to refresh mVar.SendUDPNotification("refresh") endif End Sub Sub PopulateIDData(mVar As Object, root As Object) unitName$ = mVar.registrySettings.unitName$ unitNamingMethod$ = mVar.registrySettings.unitNamingMethod$ unitDescription$ = mVar.registrySettings.unitDescription$ elem = root.AddElement("unitName") elem.SetBody(unitName$) elem = root.AddElement("unitNamingMethod") elem.SetBody(unitNamingMethod$) elem = root.AddElement("unitDescription") elem.SetBody(unitDescription$) elem = root.AddElement("serialNumber") elem.SetBody(mVar.sysInfo.deviceUniqueID$) elem = root.AddElement("functionality") elem.SetBody(mVar.lwsConfig$) elem = root.AddElement("autorunVersion") elem.SetBody(mVar.sysInfo.autorunVersion$) elem = root.AddElement("firmwareVersion") elem.SetBody(mVar.sysInfo.deviceFWVersion$) End Sub Function newUDPItem(udpLabel As Object, udpEventName As Object) As Object udpItem = {} udpItem.action$ = udpEventName udpItem.label$ = udpLabel return udpItem End Function Sub PopulateUDPData(mVar As Object, root As Object) sign = mVar.sign elem = root.AddElement("udpNotificationAddress") elem.SetBody(mVar.udpNotificationAddress$) elem = root.AddElement("notificationPort") elem.SetBody(StripLeadingSpaces(stri(mVar.udpNotificationPort%))) elem = root.AddElement("destinationPort") elem.SetBody(StripLeadingSpaces(stri(mVar.udpNotificationPort%))) if type(sign) = "roAssociativeArray" then elem = root.AddElement("receivePort") elem.SetBody(StripLeadingSpaces(stri(sign.udpReceivePort))) udpEvents = { } for each zoneHSM in sign.zonesHSM for each stateName in zoneHSM.stateTable state = zoneHSM.stateTable[stateName] udpEventsInState = state.udpEvents if type(udpEventsInState) = "roAssociativeArray" then for each udpEventName in udpEventsInState transition = udpEventsInState.Lookup(udpEventName) if type(transition.udpExport) = "roBoolean" and transition.udpExport then if type(transition.udpLabel$) = "roString" then udpLabel$ = transition.udpLabel$ else udpLabel$ = udpEventName endif if not udpEvents.DoesExist(udpLabel$) then udpEvents.AddReplace(udpLabel$, newUDPItem(udpLabel$, udpEventName)) endif endif if udpEventName = "" or udpEventName = "(.*)" then targetMediaState$ = udpEventsInState.Lookup(udpEventName).targetMediaState$ if targetMediaState$ <> "" then targetState = zoneHSM.stateTable[targetMediaState$] if targetState.type$ = "playFile" then filesTable = targetState.filesTable for each playFileKey in filesTable entry = filesTable[playFileKey] export = true if entry.DoesExist("export") and (not entry["export"]) then export = false endif if export then udpLabel$ = playFileKey if entry.DoesExist("label$") then udpLabel$ = entry["label$"] endif if not udpEvents.DoesExist(udpLabel$) then udpEvents.AddReplace(udpLabel$, newUDPItem(udpLabel$, playFileKey)) endif endif next endif endif endif next endif if state.type$ = "rfInputChannel" then if type(state.channelUpEvent) = "roAssociativeArray" and IsString(state.channelUpEvent.udpUserEvent$) and type(state.channelUpEvent.udpExport) = "roBoolean" and state.channelUpEvent.udpExport then udpLabel$ = state.channelUpEvent.udpLabel$ if not udpEvents.DoesExist(udpLabel$) then udpEvents.AddReplace(udpLabel$, newUDPItem(udpLabel$, state.channelUpEvent.udpUserEvent$)) endif endif if type(state.channelDownEvent) = "roAssociativeArray" and IsString(state.channelDownEvent.udpUserEvent$) and type(state.channelDownEvent.udpExport) = "roBoolean" and state.channelDownEvent.udpExport then udpLabel$ = state.channelDownEvent.udpLabel$ if not udpEvents.DoesExist(udpLabel$) then udpEvents.AddReplace(udpLabel$, newUDPItem(udpLabel$, state.channelDownEvent.udpUserEvent$)) endif endif endif next next udpEventsElem = root.AddElement("udpEvents") udpEventsElem.AddAttribute("useLabel","true") for each udpEvent in udpEvents udpItem = udpEvents.Lookup(udpEvent) udpEventElem = udpEventsElem.AddElement("udpEvent") udpEventLabel = udpEventElem.AddElement("label") udpEventLabel.SetBody(udpItem.label$) udpEventAction = udpEventElem.AddElement("action") udpEventAction.SetBody(udpItem.action$) next endif End Sub Sub GetID(userData as Object, e as Object) mVar = userData.mVar ' print "respond to GetID request" root = CreateObject("roXMLElement") root.SetName("BrightSignID") PopulateIDData(mVar, root) xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetUDPEvents(userData as Object, e as Object) mVar = userData.mVar root = CreateObject("roXMLElement") root.SetName("BrightSignUDPEvents") PopulateUDPData(mVar, root) xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetRemoteData(userData as Object, e as Object) mVar = userData.mVar ' print "respond to GetRemoteData request" root = CreateObject("roXMLElement") root.SetName("BrightSignRemoteData") PopulateIDData(mVar, root) PopulateUDPData(mVar, root) elem = root.AddElement("contentPort") elem.SetBody("8008") xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Function GetUserVariables(mVar As Object) As Object userVariablesList = mVar.GetOrderedVariables(mVar.activePresentation$) ' restrict returned user variables to those in the presentation filteredUserVariables = [] for each userVariable in userVariablesList if mVar.currentUserVariables.DoesExist(userVariable.name$) then uv = mVar.currentUserVariables.Lookup(userVariable.name$) if uv.position% <> -1 then userVariable.position% = uv.position% filteredUserVariables.push(userVariable) endif endif next if mVar.sign.alphabetizeVariableNames then BubbleSortUserVariables(filteredUserVariables, "name$") else BubbleSortUserVariables(filteredUserVariables, "position%") endif return filteredUserVariables End Function Sub BubbleSortUserVariables(userVariables As Object, sortKey$ As String) if type(userVariables) = "roArray" then n = userVariables.Count() while n <> 0 newn = 0 for i = 1 to (n - 1) if userVariables[i-1].Lookup(sortKey$) > userVariables[i].Lookup(sortKey$) then k = userVariables[i] userVariables[i] = userVariables[i-1] userVariables[i-1] = k newn = i endif next n = newn end while endif End Sub Sub PopulateUserVarData(mVar As Object, root As Object) userVariables = GetUserVariables(mVar) for each userVariable in userVariables variableName = userVariable.name$ elem = root.AddElement("BrightSignVar") elem.AddAttribute("name",variableName) elem.SetBody(userVariable.GetCurrentValue()) next End Sub Sub GetUserVars(userData as Object, e as Object) mVar = userData.mVar e.AddResponseHeader("Content-type", "text/xml; charset=utf-8") root = CreateObject("roXMLElement") root.SetName("BrightSignUserVariables") PopulateUserVarData(mVar, root) xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetUserVariableCategories(userData as Object, e as Object) mVar = userData.mVar e.AddResponseHeader("Content-type", "text/xml; charset=utf-8") root = CreateObject("roXMLElement") root.SetName("BrightSignUserVariableCategories") userVariableCategoryList = mVar.GetUserVariableCategoryList(mVar.activePresentation$) for each userVariableCategory in userVariableCategoryList elem = root.AddElement("BrightSignUserVariableCategory") elem.SetBody(userVariableCategory) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub UpdateFeedByCategory(userData as Object, e as Object) mVar = userData.mVar categoryName$ = e.GetRequestParam("CategoryName") updateDataFeedByCategoryMsg = CreateObject("roAssociativeArray") updateDataFeedByCategoryMsg["EventType"] = "UPDATE_DATA_FEED_BY_CATEGORY" updateDataFeedByCategoryMsg["Name"] = categoryName$ mVar.msgPort.PostMessage(updateDataFeedByCategoryMsg) e.SendResponse(200) End Sub Sub UpdateAllFeeds(userData as Object, e as Object) mVar = userData.mVar updateDataFeedByCategoryMsg = CreateObject("roAssociativeArray") updateDataFeedByCategoryMsg["EventType"] = "UPDATE_ALL_DATA_FEEDS" mVar.msgPort.PostMessage(updateDataFeedByCategoryMsg) e.SendResponse(200) End Sub Sub GetUserVariablesByCategory(userData as Object, e as Object) mVar = userData.mVar e.AddResponseHeader("Content-type", "text/xml; charset=utf-8") root = CreateObject("roXMLElement") root.SetName("BrightSignUserVariablesByCategory") categoryName$ = e.GetRequestParam("CategoryName") if categoryName$ <> "" then userVariablesByCategoryList = mVar.GetUserVariablesByCategoryList(categoryName$) for each userVariable in userVariablesByCategoryList variableName = userVariable.name$ elem = root.AddElement("BrightSignVar") elem.AddAttribute("name",variableName) elem.AddAttribute("mediaUrl", userVariable.mediaUrl$) elem.SetBody(userVariable.GetCurrentValue()) next endif xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetCurrentStatus(userData as Object, e as Object) mVar = userData.mVar ' print "respond to GetCurrentStatus request" root = CreateObject("roXMLElement") root.SetName("BrightSignStatus") autorunVersion$ = mVar.sysInfo.autorunVersion$ unitName$ = mVar.registrySettings.unitName$ unitNamingMethod$ = mVar.registrySettings.unitNamingMethod$ unitDescription$ = mVar.registrySettings.unitDescription$ elem = root.AddElement("unitName") elem.AddAttribute("label", "Unit Name") elem.SetBody(unitName$) elem = root.AddElement("unitNamingMethod") elem.AddAttribute("label", "Unit Naming Method") elem.SetBody(unitNamingMethod$) elem = root.AddElement("unitDescription") elem.AddAttribute("label", "Unit Description") elem.SetBody(unitDescription$) modelObject = CreateObject("roDeviceInfo") elem = root.AddElement("model") elem.AddAttribute("label", "Model") elem.SetBody(modelObject.GetModel()) elem = root.AddElement("firmware") elem.AddAttribute("label", "Firmware") elem.SetBody(modelObject.GetVersion()) elem = root.AddElement("autorun") elem.AddAttribute("label", "Autorun") elem.SetBody(mVar.sysInfo.autorunVersion$) elem = root.AddElement("serialNumber") elem.AddAttribute("label", "Serial Number") elem.SetBody(modelObject.GetDeviceUniqueId()) elem = root.AddElement("functionality") elem.AddAttribute("label", "Functionality") elem.SetBody(mVar.lwsConfig$) ' 86400 seconds per day deviceUptime% = modelObject.GetDeviceUptime() numDays% = deviceUptime% / 86400 numHours% = (deviceUptime% - (numDays% * 86400)) / 3600 numMinutes% = (deviceUptime% - (numDays% * 86400) - (numHours% * 3600)) / 60 numSeconds% = deviceUptime% - (numDays% * 86400) - (numHours% * 3600) - (numMinutes% * 60) deviceUptime$ = "" if numDays% > 0 then deviceUptime$ = stri(numDays%) + " days " if numHours% > 0 then deviceUptime$ = deviceUptime$ + stri(numHours%) + " hours " if numMinutes% > 0 then deviceUptime$ = deviceUptime$ + stri(numMinutes%) + " minutes " if numSeconds% > 0 then deviceUptime$ = deviceUptime$ + stri(numSeconds%) + " seconds" elem = root.AddElement("deviceUptime") elem.AddAttribute("label", "Device Uptime") elem.SetBody(deviceUptime$) elem = root.AddElement("activePresentation") elem.AddAttribute("label", "Active Presentation") elem.SetBody(mVar.activePresentation$) ' xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) xml = root.GenXML({ header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetIDInfoPage(userData as Object, e as Object) mVar = userData.mVar ' print "respond to GetIDInfoPage request" e.AddResponseHeader("Content-type", "text/html; charset=utf-8") deviceIdWebPageFilePath$ = GetPoolFilePath(mVar.assetPoolFiles, "_deviceIdWebPage.html") webPageContents$ = ReadAsciiFile(deviceIdWebPageFilePath$) e.SetResponseBodyString(webPageContents$) if not e.SendResponse(200) then stop End Sub Sub FilePosted(userData as Object, e as Object) ' print "respond to FilePosted request" destinationFilename = e.GetRequestHeader("Destination-Filename") currentDir$ = "pool/" poolDepth% = 2 while poolDepth% > 0 newDir$ = Left(Right(destinationFilename, poolDepth%), 1) currentDir$ = currentDir$ + newDir$ + "/" CreateDirectory(currentDir$) poolDepth% = poolDepth% - 1 end while regex = CreateObject("roRegEx","/","i") fileParts = regex.Split(destinationFilename) fullFilePath$ = currentDir$ + fileParts[1] MoveFile(e.GetRequestBodyFile(), fullFilePath$) e.SetResponseBodyString("RECEIVED") e.SendResponse(200) End Sub Function GetContentFiles(topDir$ As String) As Object allFiles = { } firstLevelDirs = MatchFiles(topDir$, "*") for each firstLevelDir in firstLevelDirs firstLevelDirSpec$ = topDir$ + firstLevelDir + "/" secondLevelDirs = MatchFiles(firstLevelDirSpec$, "*") for each secondLevelDir in secondLevelDirs secondLevelDirSpec$ = firstLevelDirSpec$ + secondLevelDir + "/" files = MatchFiles(secondLevelDirSpec$, "*") for each file in files allFiles.AddReplace(file, secondLevelDirSpec$) next next next return allFiles End Function 'endregion 'region Sync Function FreeSpaceOnDrive() As Object filesToPublish$ = ReadAsciiFile("filesToPublish.xml") if filesToPublish$ = "" then stop ' files that need to be copied by BrightAuthor filesToCopy = CreateObject("roAssociativeArray") ' files that can be deleted to make room for more content deletionCandidates = CreateObject("roAssociativeArray") oldLocationDeletionCandidates = CreateObject("roAssociativeArray") ' create list of files already on the card listOfOldPoolFiles = MatchFiles("/pool", "*") for each file in listOfOldPoolFiles oldLocationDeletionCandidates.AddReplace(file, file) next listOfPoolFiles = GetContentFiles("/pool/") for each file in listOfPoolFiles deletionCandidates.AddReplace(file, listOfPoolFiles.Lookup(file)) next ' create the list of files that need to be copied. this is the list of files in filesToPublish that are not in listOfPoolFiles filesToPublish = CreateObject("roXMLElement") filesToPublish.Parse(filesToPublish$) ' determine total space required totalSpaceRequired! = 0 for each fileXML in filesToPublish.file fullFileName$ = fileXML.fullFileName.GetText() o = deletionCandidates.Lookup(fullFileName$) if not IsString(o) then ' file is not already on the card fileItem = CreateObject("roAssociativeArray") fileItem.fileName$ = fileXML.fileName.GetText() fileItem.filePath$ = fileXML.filePath.GetText() fileItem.hashValue$ = fileXML.hashValue.GetText() fileItem.fileSize$ = fileXML.fileSize.GetText() filesToCopy.AddReplace(fullFileName$, fileItem) ' files that need to be copied to the card fileSize% = val(fileItem.fileSize$) totalSpaceRequired! = totalSpaceRequired! + fileSize% endif next filesToPublish = invalid ' determine if additional space is required du = CreateObject("roStorageInfo", "./") freeInMegabytes! = du.GetFreeInMegabytes() totalFreeSpace! = freeInMegabytes! * 1048576 ' print "totalFreeSpace = "; totalFreeSpace!;", totalSpaceRequired = ";totalSpaceRequired! if m.limitStorageSpace then budgetedMaximumPoolSize = 0 if m.spaceLimitedByAbsoluteSize = "true" then budgetedMaximumPoolSize = m.publishedDataSizeLimitMB * (1024.0 * 1024.0) else totalCardSize = du.GetSizeInMegabytes() * (1024.0 * 1024.0) publishedDataSizeLimitPercentage% = m.publishedDataSizeLimitPercentage budgetedMaximumPoolSize = publishedDataSizeLimitPercentage% * totalCardSize / 100.0 endif ' calculate the space that will be used after the files are copied over (size of existing pool + size of files that are getting copied over) ' totalSpaceRequired! = size of files that are getting copied over totalSizeOfPoolAfterCopy! = totalSpaceRequired! ' units are bytes for each file in listOfPoolFiles relativePath$ = listOfPoolFiles.Lookup(file) fullPath$ = relativePath$ + file ' get size of file size% = GetFileSize( fullPath$ ) totalSizeOfPoolAfterCopy! = totalSizeOfPoolAfterCopy! + size% next endif deleteUnneededFiles = false if totalFreeSpace! < totalSpaceRequired! then deleteUnneededFiles = true endif if m.limitStorageSpace then if totalSizeOfPoolAfterCopy! > budgetedMaximumPoolSize then deleteUnneededFiles = true endif endif if deleteUnneededFiles then ' parse local-sync.xml - remove its files from deletionCandidates localSync$ = ReadAsciiFile("local-sync.xml") if localSync$ <> "" then localSync = CreateObject("roXMLElement") localSync.Parse(localSync$) for each fileXML in localSync.files.download hashValue$ = fileXML.hash.GetText() hashMethod$ = fileXML.hash@method fileName$ = hashMethod$ + "-" + hashValue$ fileExisted = deletionCandidates.Delete(fileName$) if not fileExisted then fileExisted = oldLocationDeletionCandidates.Delete(fileName$) endif next endif ' parse filesToPublish.xml - remove its files from deletionCandidates filesToPublish = CreateObject("roXMLElement") filesToPublish.Parse(filesToPublish$) for each fileXML in filesToPublish.file fullFileName$ = fileXML.fullFileName.GetText() fileExisted = deletionCandidates.Delete(fullFileName$) next ' delete all files that used the old style pool strategy that aren't currently in use for each fileToDelete in oldLocationDeletionCandidates pathOnCard$ = "/pool/" + fileToDelete oldLocationDeletionCandidates.Delete(fileToDelete) ok = DeleteFile(pathOnCard$) next ' delete files from deletionCandidates until totalFreeSpace! > totalSpaceRequired! ' if the user has limited storage space for pool files, delete content until that limitation is reached for each fileToDelete in deletionCandidates path$ = deletionCandidates.Lookup(fileToDelete) pathOnCard$ = path$ + fileToDelete if m.limitStorageSpace then size% = GetFileSize( pathOnCard$ ) totalSizeOfPoolAfterCopy! = totalSizeOfPoolAfterCopy! - size% endif deletionCandidates.Delete(fileToDelete) DeleteFile(pathOnCard$) continueDeleting = false if m.limitStorageSpace then if totalSizeOfPoolAfterCopy! > budgetedMaximumPoolSize then continueDeleting = true endif else du = invalid du = CreateObject("roStorageInfo", "./") freeInMegabytes! = du.GetFreeInMegabytes() totalFreeSpace! = freeInMegabytes! * 1048576 ' print "Delete file ";pathOnCard$ ' print "totalFreeSpace = "; totalFreeSpace!;", totalSpaceRequired = ";totalSpaceRequired! if totalFreeSpace! <= totalSpaceRequired! then continueDeleting = true endif endif if not continueDeleting then return filesToCopy endif next ' the way this code is currently written, this method will return 'fail' if we can't delete enough files to get to the budgeted amount return "fail" endif return filesToCopy End Function Function GetFileSize( filePath$ As String ) As Integer size = 0 checkFile = CreateObject("roReadFile", filePath$) if (checkFile <> invalid) then checkFile.SeekToEnd() size = checkFile.CurrentPosition() checkFile = invalid endif return size End Function Sub SpecifyCardSizeLimits(userData as Object, e as Object) mVar = userData.mVar ' = bsp limitStorageSpace = lcase(e.GetRequestParam("limitStorageSpace")) if limitStorageSpace = "true" then mVar.limitStorageSpace = true mVar.spaceLimitedByAbsoluteSize = lcase(e.GetRequestParam("spaceLimitedByAbsoluteSize")) mVar.publishedDataSizeLimitMB = ConvertToInt(e.GetRequestParam("publishedDataSizeLimitMB")) mVar.publishedDataSizeLimitPercentage = ConvertToInt(e.GetRequestParam("publishedDataSizeLimitPercentage")) mVar.dynamicDataSizeLimitMB = ConvertToInt(e.GetRequestParam("dynamicDataSizeLimitMB")) mVar.dynamicDataSizeLimitPercentage = ConvertToInt(e.GetRequestParam("dynamicDataSizeLimitPercentage")) mVar.htmlDataSizeLimitPercentage = ConvertToInt(e.GetRequestParam("htmlDataSizeLimitPercentage")) mVar.htmlDataSizeLimitMB = ConvertToInt(e.GetRequestParam("htmlDataSizeLimitMB")) else mVar.limitStorageSpace = false endif e.SetResponseBodyString("") if not e.SendResponse(200) then stop End Sub Function ConvertToInt(str As string) As Integer if str = "" then return 0 endif return int(val(str)) End Function Sub PrepareForTransfer(userData as Object, e as Object) mVar = userData.mVar ' print "respond to PrepareForTransfer request" MoveFile(e.GetRequestBodyFile(), "filesToPublish.xml") filesToCopy = mVar.FreeSpaceOnDrive() if type(filesToCopy) = "roAssociativeArray" then root = CreateObject("roXMLElement") root.SetName("filesToCopy") for each key in filesToCopy fileItem = filesToCopy[key] item = root.AddBodyElement() item.SetName("file") elem = item.AddElement("fileName") elem.SetBody(fileItem.fileName$) elem = item.AddElement("filePath") elem.SetBody(fileItem.filePath$) elem = item.AddElement("hashValue") elem.SetBody(fileItem.hashValue$) elem = item.AddElement("fileSize") elem.SetBody(fileItem.fileSize$) next xml = root.GenXML({ header: true }) e.SetResponseBodyString(xml) e.SendResponse(200) else ' the following call is ignored on a post ' e.SetResponseBodyString("413") e.SendResponse(413) endif End Sub Sub SyncSpecPosted(userData as Object, e as Object) EVENT_REALIZE_SUCCESS = 101 mVar = userData.mVar ' print "respond to SyncSpecPosted request" ' MoveFile(e.GetRequestBodyFile(), "tmp:new-sync.xml") MoveFile(e.GetRequestBodyFile(), "new-sync.xml") e.SetResponseBodyString("RECEIVED") e.SendResponse(200) oldSync = CreateObject("roSyncSpec") ok = oldSync.ReadFromFile("local-sync.xml") if not ok then stop newSync = CreateObject("roSyncSpec") ok = newSync.ReadFromFile("new-sync.xml") if not ok then stop oldSyncSpecScriptsOnly = oldSync.FilterFiles("download", { group: "script" } ) newSyncSpecScriptsOnly = newSync.FilterFiles("download", { group: "script" } ) ' listOfDownloadFiles = newSyncSpecScriptsOnly.GetFileList("download") ' for each downloadFile in listOfDownloadFiles ' print "name = ";downloadFile.name ' print "size = ";downloadFile.size ' print "hash = ";downloadFile.hash ' next mVar.diagnostics.PrintTimestamp() mVar.diagnostics.PrintDebug("### LWS DOWNLOAD COMPLETE") mVar.assetCollection = newSync.GetAssets("download") mVar.assetPoolFiles = CreateObject("roAssetPoolFiles", mVar.assetPool, mVar.assetCollection) if type(mVar.assetPoolFiles) <> "roAssetPoolFiles" then stop rebootRequired = false if not oldSyncSpecScriptsOnly.FilesEqualTo(newSyncSpecScriptsOnly) then ' Protect all the media files that the current sync spec is using in case we fail part way ' through and need to continue using it. if not (mVar.assetPool.ProtectAssets("current", oldSync) and mVar.assetPool.ProtectAssets("new", newSync)) then print "Failed to protect files that we need in the pool" stop endif realizer = CreateObject("roAssetRealizer", mVar.assetPool, "/") globalAA = GetGlobalAA() globalAA.bsp.msgPort.DeferWatchdog(120) event = realizer.Realize(newSyncSpecScriptsOnly) realizer = invalid if event.GetEvent() <> EVENT_REALIZE_SUCCESS then mVar.logging.WriteDiagnosticLogEntry(mVar.diagnosticCodes.EVENT_REALIZE_FAILURE, stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason()) mVar.diagnostics.PrintDebug("### Realize failed " + stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason() ) DeleteFile("new-sync.xml") newSync = invalid return else rebootRequired = true endif endif if not newSync.WriteToFile("local-sync.xml") then stop ' cause fsync CreateObject("roReadFile", "local-sync.xml") if rebootRequired then mVar.diagnostics.PrintDebug("### new script or upgrade found - reboot") RebootSystem() endif globalAA = GetGlobalAA() globalAA.autoscheduleFilePath$ = GetPoolFilePath(mVar.assetPoolFiles, "autoschedule.xml") globalAA.resourcesFilePath$ = GetPoolFilePath(mVar.assetPoolFiles, "resources.txt") globalAA.boseProductsFilePath$ = GetPoolFilePath(mVar.assetPoolFiles, "BoseProducts.xml") if globalAA.autoscheduleFilePath$ = "" then stop mVar.SetPoolSizes(newSync) DeleteFile("new-sync.xml") newSync = invalid ' send internal message to prepare for restart prepareForRestartEvent = CreateObject("roAssociativeArray") prepareForRestartEvent["EventType"] = "PREPARE_FOR_RESTART" mVar.msgPort.PostMessage(prepareForRestartEvent) ' send internal message indicating that new content is available contentUpdatedEvent = CreateObject("roAssociativeArray") contentUpdatedEvent["EventType"] = "CONTENT_UPDATED" mVar.msgPort.PostMessage(contentUpdatedEvent) End Sub 'endregion 'region Presentation Sub ConfigureGPIOInput(buttonNumber$ As String) buttonNumber% = int(val(buttonNumber$)) m.gpioStateMachineRequired[buttonNumber%] = true End Sub Sub ConfigureGPIOButton(buttonNumber$ As String, gpioSpec As Object) buttonNumber% = int(val(buttonNumber$)) if type(m.gpioSM[buttonNumber%]) = "roAssociativeArray" then m.gpioSM[buttonNumber%].ConfigureButton(gpioSpec) endif End Sub Sub ConfigureBPInput(buttonPanelIndex% As Integer, buttonNumber$ As String) if buttonNumber$ = "-1" then for i% = 0 to 10 m.bpStateMachineRequired[buttonPanelIndex%, i%] = true next else buttonNumber% = int(val(buttonNumber$)) m.bpStateMachineRequired[buttonPanelIndex%, buttonNumber%] = true m.bpInputUsed[buttonPanelIndex%, buttonNumber%] = true endif End Sub Sub ConfigureBPButton(buttonPanelIndex% As Integer, buttonNumber$ As String, bpSpec As Object) if buttonNumber$ = "-1" then for i% = 0 to 10 if type(m.bpSM[buttonPanelIndex%, i%]) = "roAssociativeArray" then m.bpSM[buttonPanelIndex%, i%].ConfigureButton(bpSpec) endif next else buttonNumber% = int(val(buttonNumber$)) if type(m.bpSM[buttonPanelIndex%, buttonNumber%]) = "roAssociativeArray" then m.bpSM[buttonPanelIndex%, buttonNumber%].ConfigureButton(bpSpec) endif endif End Sub Function NewGlobalVariables() As Object globalVariables = CreateObject("roAssociativeArray") globalVariables.language$ = "eng" return globalVariables End Function Function newPresentation(bsp As Object, presentationXML As Object) As Object presentation = {} presentation.name$ = presentationXML.name.GetText() presentation.presentationName$ = presentationXML.presentationName.GetText() presentation.path$ = presentationXML.path.GetText() ' for earlier 3.7 presentations if presentation.presentationName$ = "" then presentation.presentationName$ = presentation.path$ endif return presentation End Function Function newScriptPlugin(scriptPluginXML As Object) As Object scriptPlugin = {} scriptPlugin.name$ = scriptPluginXML.name.GetText() scriptPlugin.plugin = invalid return scriptPlugin End Function Function newHTMLSite(bsp As Object, htmlSiteXML As Object) As Object htmlSite = {} htmlSite.name$ = htmlSiteXML.name.GetText() if htmlSiteXML.GetName() = "localHTMLSite" then htmlSite.prefix$ = htmlSiteXML.prefix.GetText() htmlSite.filePath$ = htmlSiteXML.filePath.GetText() htmlSite.contentIsLocal = true else if htmlSiteXML.GetName() = "remoteHTMLSite" then htmlSite.url = newParameterValue(bsp, htmlSiteXML.url.parameterValue) htmlSite.contentIsLocal = false endif return htmlSite End Function Function newLiveDataFeed(bsp As Object, liveDataFeedXML As Object) As Object liveDataFeed = InitializeLiveDataFeedParameters( bsp ) liveDataFeed.name$ = CleanName( liveDataFeedXML.name.GetText() ) liveBSNDataFeedXMLList = liveDataFeedXML.GetNamedElements("liveBSNDataFeed") liveDynamicPlaylistXMLList = liveDataFeedXML.GetNamedElements("liveDynamicPlaylist") liveDataFeed.isDynamicPlaylist = false if liveBSNDataFeedXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveBSNDataFeed.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) else if liveDynamicPlaylistXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveDynamicPlaylist.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) liveDataFeed.isDynamicPlaylist = true else liveDataFeed.url = newParameterValue(bsp, liveDataFeedXML.url.parameterValue) endif liveDataFeed.parser$ = liveDataFeedXML.parserFunctionName.GetText() liveDataFeed.uvParser$ = liveDataFeedXML.uvParserFunctionName.GetText() liveDataFeed.updateInterval% = int(val(liveDataFeedXML.updateInterval.GetText())) downloadContent$ = LCase(liveDataFeedXML.downloadContent.GetText()) if downloadContent$ = "true" then liveDataFeed.downloadContent = true else liveDataFeed.downloadContent = false endif liveDataFeed.autoGenerateUserVariables = false if liveDataFeedXML.autoGenerateUserVariables.Count() = 1 and LCase(liveDataFeedXML.autoGenerateUserVariables.GetText()) = "true" then liveDataFeed.autoGenerateUserVariables = true endif liveDataFeed.userVariableAccess$ = "private" if liveDataFeedXML.userVariableAccess.Count() = 1 and LCase(liveDataFeedXML.userVariableAccess.GetText()) = "shared" then liveDataFeed.userVariableAccess$ = "shared" endif SetLiveDataFeedHandlers( liveDataFeed ) return liveDataFeed End Function Function newLiveDataFeedFromMRSSFormat( bsp As Object, liveDataFeedName$ As String, url$ As String, isDynamicPlaylist As Boolean ) As Object liveDataFeed = InitializeLiveDataFeedParameters( bsp ) liveDataFeed.name$ = liveDataFeedName$ liveDataFeed.url = newTextParameterValue(url$) liveDataFeed.updateInterval% = 900 ' default to 15 minutes. If the feed has a TTL value that is lower, use that. liveDataFeed.isDynamicPlaylist = isDynamicPlaylist SetLiveDataFeedHandlers( liveDataFeed ) return liveDataFeed End Function Function newLiveDataFeedFromOldDataFormat(bsp As Object, url As Object, updateInterval% As Integer) As Object liveDataFeed = InitializeLiveDataFeedParameters( bsp ) liveDataFeed.name$ = url.GetCurrentParameterValue() liveDataFeed.url = url liveDataFeed.updateInterval% = updateInterval% SetLiveDataFeedHandlers( liveDataFeed ) return liveDataFeed End Function Function newLiveDataFeedWithAuthData(bsp As Object, url As Object, authData As Object, updateInterval% As Integer) As Object liveDataFeed = InitializeLiveDataFeedParameters( bsp ) liveDataFeed.name$ = CleanName( url.GetCurrentParameterValue() ) liveDataFeed.url = url liveDataFeed.authenticationData = authData liveDataFeed.updateInterval% = updateInterval% SetLiveDataFeedHandlers( liveDataFeed ) return liveDataFeed End Function Function InitializeLiveDataFeedParameters( bsp As Object ) liveDataFeed = {} liveDataFeed.bsp = bsp liveDataFeed.name$ = "" liveDataFeed.title$ = "" liveDataFeed.url = "" liveDataFeed.parser$ = "" liveDataFeed.uvParser$ = "" liveDataFeed.updateInterval% = 0 liveDataFeed.downloadContent = false liveDataFeed.isMRSSFeed = false liveDataFeed.isDynamicPlaylist = false liveDataFeed.autoGenerateUserVariables = false liveDataFeed.forceUpdate = false liveDataFeed.restrictNumberOfItems = false liveDataFeed.numberOfItemsToDisplay = -1 return liveDataFeed End Function Sub SetLiveDataFeedHandlers( liveDataFeed As Object ) liveDataFeed.ReadFeedContent = ReadFeedContent liveDataFeed.ReadLiveFeedContent = ReadLiveFeedContent liveDataFeed.ReadMRSSContent = ReadMRSSContent liveDataFeed.DownloadLiveFeedContent = DownloadLiveFeedContent liveDataFeed.DownloadMRSSContent = DownloadMRSSContent liveDataFeed.ParseMRSSFeed = ParseMRSSFeed liveDataFeed.FeedIsMRSS = FeedIsMRSS liveDataFeed.RestartLiveDataFeedDownloadTimer = RestartLiveDataFeedDownloadTimer liveDataFeed.HandleLiveDataFeedContentDownloadAssetFetcherEvent = HandleLiveDataFeedContentDownloadAssetFetcherEvent End Sub Function CleanName(input As String) As String charsToReplace = [ "/", ":", ",", ".", "&", "=", "?"] output = input for each charToReplace in charsToReplace index = 1 while index <> 0 index = instr(1, output, charToReplace) if index <> 0 then part1 = "" if (index - 1) > 0 then part1 = mid(output, 0, index - 1) endif part2 = "" if (len(output) - index) > 0 then part2 = mid(output, index + 1, len(output) - index) endif output = part1 + "-" + part2 endif end while next return output End Function Sub Restart(presentationName$ As String) m.restartPendingMediaEnd = false m.dontChangePresentationUntilMediaEndEventReceived = false m.currentUserVariables = {} m.liveDataFeeds = { } m.presentations = { } m.htmlSites = { } m.scriptPlugins = CreateObject("roArray", 1, true) m.additionalPublishedFiles = CreateObject("roArray", 1, true) m.IsSyncMaster = false m.IsSyncSlave = false m.rfChannelName = "" m.rfVirtualChannel = "" m.tunerScanPercentageComplete$ = "0" for n% = 0 to 2 for i% = 0 to 10 m.bpStateMachineRequired[n%, i%] = false m.bpInputUsed[n%, i%] = false m.bpOutputUsed[n%, i%] = false next next for i% = 0 to 7 m.gpioStateMachineRequired[i%] = false next if presentationName$ = "" then globalAA = GetGlobalAA() xmlAutoscheduleFile$ = ReadAsciiFile(globalAA.autoscheduleFilePath$) if xmlAutoscheduleFile$ <> "" then schedule = m.XMLAutoschedule(globalAA.autoscheduleFilePath$) else stop endif if type(schedule.activeScheduledEvent) = "roAssociativeArray" then xmlFileName$ = schedule.autoplayPoolFile$ else xmlFileName$ = "" endif m.schedule = schedule if (xmlFileName$ <> "") then presentationName$ = schedule.activeScheduledEvent.presentationName$ endif else autoplayFileName$ = "autoplay-" + presentationName$ + ".xml" xmlFileName$ = m.assetPoolFiles.GetPoolFilePath(autoplayFileName$) m.activePresentation$ = presentationName$ endif if (xmlFileName$ <> "") then BrightAuthor = CreateObject("roXMLElement") BrightAuthor.Parse(ReadAsciiFile(xmlFileName$)) ' verify that this is a valid BrightAuthor XML file if BrightAuthor.GetName() <> "BrightAuthor" then print "Invalid XML file - name not BrightAuthor" : stop if not IsString(BrightAuthor@version) then print "Invalid XML file - version not found" : stop m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("### create sign object") version% = int(val(BrightAuthor@version)) sign = newSign(BrightAuthor, m.globalVariables, m, m.msgPort, m.controlPort, version%) m.LogActivePresentation() else sign = invalid ' set idle screen color b = CreateObject("roByteArray") b.FromHexString(m.registrySettings.idleScreenColor$) color_spec% = (255*256*256*256) + (b[1]*256*256) + (b[2]*256) + b[3] m.diagnostics.PrintDebug("Set idle screen color: red = " + stri(b[1]) + ", green = " + stri(b[2]) + ", blue = " + stri(b[3])) videoMode = CreateObject("roVideoMode") videoMode.SetBackgroundColor(color_spec%) videoMode = invalid endif if type(m.sign) = "roAssociativeArray" and type(m.sign.zonesHSM) = "roArray" then for each zoneHSM in m.sign.zonesHSM if IsAudioPlayer(zoneHSM.audioPlayer) then zoneHSM.audioPlayer.Stop() zoneHSM.audioPlayer = invalid endif if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Stop() zoneHSM.videoPlayer = invalid endif next endif zoneHSM = invalid m.dispatchingZone=invalid m.sign = invalid RunGarbageCollector() m.ClearImageBuffers() m.sign = sign ' create required GPIO state machines for i% = 0 to 7 if m.gpioStateMachineRequired[i%] then m.gpioSM[i%] = newGPIOStateMachine(m, m.controlPort, m.controlPortIdentity, i%) else m.gpioSM[i%] = invalid endif next ' create, initialize, and configure required BP state machines and BP's for buttonPanelIndex% = 0 to 2 if type(m.bpInputPorts[buttonPanelIndex%]) = "roControlPort" then configuration% = m.bpInputPortConfigurations[buttonPanelIndex%] else configuration% = 0 endif for i% = 0 to 10 if (configuration% and (2 ^ i%)) <> 0 then forceUsed = true else forceUsed = false endif ' if m.bpStateMachineRequired[buttonPanelIndex%, i%] then if (m.bpInputUsed[buttonPanelIndex%, i%] or forceUsed) and IsString(m.bpInputPortIdentities[buttonPanelIndex%]) and type(m.bpInputPorts[buttonPanelIndex%]) = "roControlPort" then m.bpSM[buttonPanelIndex%, i%] = newBPStateMachine(m, m.bpInputPortIdentities[buttonPanelIndex%], buttonPanelIndex%, i%) else m.bpSM[buttonPanelIndex%, i%] = invalid endif next next m.ConfigureBPs() for buttonPanelIndex% = 0 to 2 if type(m.bpOutputSetup[buttonPanelIndex%]) = "roControlPort" then configuration% = m.bpInputPortConfigurations[buttonPanelIndex%] ' set bits in our mask for buttons we want to disable (not enable!) loopFlag% = 1 buttonFlag% = 0 for i% = 0 to 10 if (configuration% and (2 ^ i%)) <> 0 then forceUsed = true else forceUsed = false endif ' if not (m.bpInputUsed[buttonPanelIndex%, i%] or m.bpOutputUsed[buttonPanelIndex%, i%]) then ' if not m.bpStateMachineRequired[buttonPanelIndex%, i%] then if not (m.bpInputUsed[buttonPanelIndex%, i%] or forceUsed) then buttonFlag% = buttonFlag% + loopFlag% endif loopFlag% = loopFlag% * 2 next ' the 1 here is the position of the mask for disabling buttons m.bpOutputSetup[buttonPanelIndex%].SetOutputValue(1, buttonFlag%) loopFlag% = 1 ledFlag% = 0 for i% = 0 to 10 if (configuration% and (2 ^ i%)) <> 0 then forceUsed = true else forceUsed = false endif if not (m.bpInputUsed[buttonPanelIndex%, i%] or m.bpOutputUsed[buttonPanelIndex%, i%] or forceUsed) then ledFlag% = ledFlag% + loopFlag% endif loopFlag% = loopFlag% * 2 next ' the 2 here signifies the mask position for LED disabling m.bpOutputSetup[buttonPanelIndex%].SetOutputValue(2, ledFlag%) endif next ' reset connector volumes m.analogVolume% = 100 m.analog2Volume% = 100 m.analog3Volume% = 100 m.hdmiVolume% = 100 m.spdifVolume% = 100 m.usbVolume% = 100 ' reclaim memory RunGarbageCollector() ' if there are script plugins associated with this sign, initialize them here userVariables = m.currentUserVariables ERR_NORMAL_END = &hFC for each scriptPlugin in m.scriptPlugins initializeFunction$ = "result = " + scriptPlugin.name$ + "_Initialize(m.msgPort, userVariables, m)" retVal = Eval(initializeFunction$) if retVal <> ERR_NORMAL_END then ' log the failure m.diagnostics.PrintDebug("Failure executing Eval to initialize script plugin file: return value = " + stri(retVal) + ", call was " + initializeFunction$) m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, stri(retVal) + chr(9) + scriptPlugin.name$) else scriptPlugin.plugin = result endif next ' user variable web server if type(m.sign) = "roAssociativeArray" and (m.registrySettings.lwsConfig$ = "c" or m.registrySettings.lwsConfig$ = "s") then lwsUserName$ = m.registrySettings.lwsUserName$ lwsPassword$ = m.registrySettings.lwsPassword$ if (len(lwsUserName$) + len(lwsPassword$)) > 0 then credentials = CreateObject("roAssociativeArray") credentials.AddReplace(lwsUserName$, lwsPassword$) else credentials = invalid end if m.sign.localServer = CreateObject("roHttpServer", { port: 8008 }) m.sign.localServer.SetPort(m.msgPort) m.sign.GetUserVarsAA = { HandleEvent: GetUserVars, mVar: m } m.sign.GetConfigurationPageAA = { HandleEvent: GetConfigurationPage, mVar: m } m.sign.GetUDPEventsAA = { HandleEvent: GetUDPEvents, mVar: m} m.sign.SendUdpRestAA = { HandleEvent: SendUdpRest, mVar: m } m.sign.SetValuesAA = { HandleEvent: SetValues, mVar: m } m.sign.SetValuesByCategoryAA = { HandleEvent: SetValuesByCategory, mVar: m } m.sign.GetUserVariableCategoriesAA = { HandleEvent: GetUserVariableCategories, mVar: m } m.sign.GetUserVariablesByCategoryAA = { HandleEvent: GetUserVariablesByCategory, mVar: m } m.sign.UpdateFeedByCategoryAA = { HandleEvent: UpdateFeedByCategory, mVar: m } m.sign.UpdateAllFeedsAA = { HandleEvent: UpdateAllFeeds, mVar: m } m.sign.localServer.AddGetFromFile({ url_path: "/GetAutorun", content_type: "text/plain; charset=utf-8", filename: "autorun.brs"}) m.sign.localServer.AddGetFromEvent({ url_path: "/GetUserVars", user_data: m.sign.GetUserVarsAA}) m.sign.localServer.AddGetFromEvent({ url_path: "/", user_data: m.sign.GetConfigurationPageAA, passwords: credentials}) m.sign.localServer.AddGetFromEvent({ url_path: "/GetUDPEvents", user_data: m.sign.GetUDPEventsAA }) m.sign.localServer.AddGetFromEvent({ url_path: "/GetUserVariableCategories", user_data: m.sign.GetUserVariableCategoriesAA}) m.sign.localServer.AddGetFromEvent({ url_path: "/GetUserVariablesByCategory", user_data: m.sign.GetUserVariablesByCategoryAA}) m.sign.localServer.AddGetFromEvent({ url_path: "/UpdateFeedByCategory", user_data: m.sign.UpdateFeedByCategoryAA}) m.sign.localServer.AddGetFromEvent({ url_path: "/UpdateAllFeeds", user_data: m.sign.UpdateAllFeedsAA}) m.sign.localServer.AddPostToFormData({ url_path: "/SetValues", user_data: m.sign.SetValuesAA, passwords: credentials}) m.sign.localServer.AddPostToFormData({ url_path: "/SetValuesByCategory", user_data: m.sign.SetValuesByCategoryAA, passwords: credentials}) m.sign.localServer.AddPostToFormData({ url_path: "/SendUDP", user_data: m.sign.SendUdpRestAA }) endif ' device web page m.deviceWebPageFilePath$ = "" if type(m.sign) = "roAssociativeArray" and m.sign.deviceWebPageDisplay$ <> "None" and type(m.sign.localServer) = "roHttpServer" then ' get all the files in the device web page ' FIX ME SOMEDAY - need a single object that represents the current sync spec currentSync = ReadSyncSpec() if not type(currentSync) = "roSyncSpec" stop if m.sign.deviceWebPageDisplay$ = "Custom" then ' files in the custom device web page start with either ' -customDeviceWebPage- ' or ' -customDeviceWebPage- customDeviceWebPageMarker$ = "-customDeviceWebPage-" downloadFiles = currentSync.GetFileList("download") for each downloadFile in downloadFiles fileName$ = downloadFile.name indexOfMarker% = instr(1, fileName$, customDeviceWebPageMarker$) if indexOfMarker% > 1 then strippedFileName$ = mid(fileName$, indexOfMarker% + len(customDeviceWebPageMarker$)) ' the main web page is ' -- or ' -customDeviceWebPage- if strippedFileName$ = m.sign.customDeviceWebPage$ then m.deviceWebPageFilePath$ = GetPoolFilePath(m.assetPoolFiles, fileName$) else ' other asset ext = GetFileExtension(strippedFileName$) if ext <> invalid then contentType$ = GetMimeTypeByExtension(ext) if contentType$ <> invalid then url$ = "/" + strippedFileName$ filePath$ = GetPoolFilePath(m.assetPoolFiles, fileName$) m.sign.localServer.AddGetFromFile({ url_path: url$, filename: filePath$, content_type: contentType$ }) endif endif endif endif next else m.deviceWebPageFilePath$ = GetPoolFilePath(m.assetPoolFiles, "_deviceWebPage.html") endif endif ' Notify controlling devices we have started playback m.SendUDPNotification("startPlayback") End Sub Function GetFileExtension(file as String) as Object s=file.tokenize(".") if s.Count()>1 ext=s.pop() return ext end if return invalid end Function Function GetMimeTypeByExtension(ext as String) as String ' start with audio types ' if ext="mp3" return "audio/mpeg" ' now image types ' else if ext="gif" return "image/gif" else if ext="jpeg" return "image/jpeg" else if ext="jpg" return "image/jpeg" else if ext="png" return "image/png" else if ext="svg" return "image/svg+xml" ' now text types' else if ext="css" return "text/css" else if ext="js" return "application/JavaScript" else if ext="csv" return "text/csv" else if ext="html" return "text/html" else if ext="htm" return "text/html" else if ext="txt" return "text/plain" else if ext="xml" return "text/xml" ' now some video types' else if ext="mpeg" return "video/mpeg" else if ext="mp4" return "video/mp4" else if ext="ts" return "video/mpeg" end if return "" end Function Sub LogActivePresentation() if type(m.activePresentation$) = "roString" then activePresentation$ = m.activePresentation$ else activePresentation$ = "" endif m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_START_PRESENTATION, activePresentation$) End Sub Sub UpdateEdidUserVariables(postMsg As Boolean) userVariables = m.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.systemVariable$ = "edidMonitorSerialNumber" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidMonitorSerialNumber$, postMsg) else if userVariable.systemVariable$ = "edidYearOfManufacture" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidYearOfManufacture$, postMsg) else if userVariable.systemVariable$ = "edidMonitorName" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidMonitorName$, postMsg) else if userVariable.systemVariable$ = "edidManufacturer" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidManufacturer$, postMsg) else if userVariable.systemVariable$ = "edidUnspecifiedText" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidUnspecifiedText$, postMsg) else if userVariable.systemVariable$ = "edidSerialNumber" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidSerialNumber$, postMsg) else if userVariable.systemVariable$ = "edidManufacturerProductCode" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidManufacturerProductCode$, postMsg) else if userVariable.systemVariable$ = "edidWeekOfManufacture" then userVariable.SetCurrentValue(m.bsp.sysInfo.edidWeekOfManufacture$, postMsg) endif next End Sub Sub UpdateIPAddressUserVariables(postMsg As Boolean) userVariables = m.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.systemVariable$ = "ipAddressWired" then userVariable.SetCurrentValue(m.sysInfo.ipAddressWired$, postMsg) else if userVariable.systemVariable$ = "ipAddressWireless" then userVariable.SetCurrentValue(m.sysInfo.ipAddressWireless$, postMsg) endif next End Sub Sub UpdateRFChannelCountUserVariables(postMsg As Boolean) userVariables = m.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.systemVariable$ = "rfChannelCount" then userVariable.SetCurrentValue(StripLeadingSpaces(stri(m.scannedChannels.Count())), postMsg) endif next End Sub Sub UpdateRFChannelUserVariables(postMsg As Boolean) userVariables = m.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.systemVariable$ = "rfChannelName" then userVariable.SetCurrentValue(m.rfChannelName, postMsg) else if userVariable.systemVariable$ = "rfVirtualChannel" then userVariable.SetCurrentValue(m.rfVirtualChannel, postMsg) endif next End Sub Sub UpdateTunerScanPercentageCompleteUserVariables(postMsg As Boolean) userVariables = m.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.systemVariable$ = "tunerScanPercentageComplete" then userVariable.SetCurrentValue(m.tunerScanPercentageComplete$, postMsg) endif next End Sub Function GetScannedChannels() As Object channelDescriptors = CreateObject("roArray", 1, true) channelManager = CreateObject("roChannelManager") if type(channelManager) = "roChannelManager" then ' Get channel descriptors for all the channels on the device channelCount% = channelManager.GetChannelCount() if channelCount% > 0 then channelInfo = CreateObject("roAssociativeArray") for channelIndex% = 0 to channelCount% - 1 channelInfo["ChannelIndex"] = channelIndex% channelDescriptor = channelManager.CreateChannelDescriptor(channelInfo) channelDescriptors.push(channelDescriptor) next endif endif return channelDescriptors End Function 'endregion 'region Bose Products Function ReadBoseProductsFile() As Object boseProductsFileXML = CreateObject("roXMLElement") globalAA = GetGlobalAA() boseProductsFileContents$ = ReadAsciiFile(globalAA.boseProductsFilePath$) if len(boseProductsFileContents$) = 0 then return 0 boseProductsFileXML.Parse(boseProductsFileContents$) ' verify that this is a valid BoseProducts XML file if boseProductsFileXML.GetName() <> "BoseProducts" then print "Invalid BoseProducts XML file - name not BoseProducts" : stop if not IsString(boseProductsFileXML@version) then print "Invalid BoseProducts XML file - version not found" : stop boseProductsXML = boseProductsFileXML.product numBoseProducts% = boseProductsXML.Count() boseProducts = CreateObject("roAssociativeArray") for each boseProductXML in boseProductsXML AddBoseProduct(boseProducts, boseProductXML) next return boseProducts End Function Sub AddBoseProduct(boseProducts As Object, boseProductXML As Object) boseProduct = CreateObject("roAssociativeArray") productName$ = boseProductXML.productName.GetText() volumeTable = CreateObject("roAssociativeArray") volumeTableElements = boseProductXML.GetNamedElements("volumeTable") if volumeTableElements.Count() <> 1 then stop volumeTableElement = volumeTableElements[0] xval1Elements = volumeTableElement.GetNamedElements("xval1") volumeTable.xval1% = int(val(xval1Elements[0].GetText())) yval1Elements = volumeTableElement.GetNamedElements("yval1") volumeTable.yval1% = int(val(yval1Elements[0].GetText())) xval2Elements = volumeTableElement.GetNamedElements("xval2") volumeTable.xval2% = int(val(xval2Elements[0].GetText())) yval2Elements = volumeTableElement.GetNamedElements("yval2") volumeTable.yval2% = int(val(yval2Elements[0].GetText())) xval3Elements = volumeTableElement.GetNamedElements("xval3") volumeTable.xval3% = int(val(xval3Elements[0].GetText())) yval3Elements = volumeTableElement.GetNamedElements("yval3") volumeTable.yval3% = int(val(yval3Elements[0].GetText())) boseProduct.volumeTable = volumeTable transportElements = boseProductXML.GetNamedElements("transport") if transportElements.Count() <> 1 then stop transportElement = transportElements[0] transportAttributes = transportElement.GetAttributes() transportType$ = transportAttributes.Lookup("type") boseProduct.protocol$ = "ASCII" if transportType$ = "Serial-Binary" then boseProduct.protocol$ = "Binary" endif baudRateElements = transportElement.GetNamedElements("baudRate") boseProduct.baudRate% = int(val(baudRateElements[0].GetText())) dataBitsElements = transportElement.GetNamedElements("dataBits") boseProduct.dataBits$ = dataBitsElements[0].GetText() parityElements = transportElement.GetNamedElements("parity") boseProduct.parity$ = parityElements[0].GetText() stopBitsElements = transportElement.GetNamedElements("stopBits") boseProduct.stopBits$ = stopBitsElements[0].GetText() invertSignalsElements = transportElement.GetNamedElements("invertSignals") invertSignals$ = invertSignalsElements[0].GetText() if invertSignals$ = "true" then boseProduct.invertSignals = true else boseProduct.invertSignals = false endif sendEol$ = "CR" sendEolElements = transportElement.GetNamedElements("sendEOL") if sendEolElements.Count() = 1 then if sendEolElements[0].GetText() <> "" then sendEol$ = sendEolElements[0].GetText() endif endif boseProduct.sendEol$ = sendEol$ receiveEol$ = "CR" receiveEolElements = transportElement.GetNamedElements("receiveEOL") if receiveEolElements.Count() = 1 then if receiveEolElements[0].GetText() <> "" then receiveEol$ = receiveEolElements[0].GetText() endif endif boseProduct.receiveEol$ = receiveEol$ boseProducts.AddReplace(productName$, boseProduct) End Sub Function GetBoseProductSpec(productName$) As Object if type(m.boseProductSpecs) = "roAssociativeArray" then return m.boseProductSpecs.Lookup(productName$) else return 0 endif End Function 'endregion 'region Schedule Function XMLAutoschedule(xmlFileName$ As String) autoScheduleXML = CreateObject("roXMLElement") autoScheduleXML.Parse(ReadAsciiFile(xmlFileName$)) ' verify that this is a valid autoschedule XML file if autoScheduleXML.GetName() <> "autoschedule" then print "Invalid autoschedule XML file - name not autoschedule" : stop if not IsString(autoScheduleXML@version) then print "Invalid autoschedule XML file - version not found" : stop ' print "autoschedule xml file - version = "; autoScheduleXML@version schedule = newSchedule(autoScheduleXML) if type(schedule.activeScheduledEvent) = "roAssociativeArray" then presentation$ = schedule.activeScheduledEvent.presentationName$ m.activePresentation$ = presentation$ autoplayFileName$ = "autoplay-" + presentation$ + ".xml" ' find the autoplay file in the pool folder currentSync = ReadSyncSpec() if not type(currentSync) = "roSyncSpec" stop assetCollection = currentSync.GetAssets("download") apf = CreateObject("roAssetPoolFiles", m.assetPool, assetCollection) autoplayPoolFile$ = apf.GetPoolFilePath(autoplayFileName$) if autoplayPoolFile$ = "" then stop schedule.autoplayPoolFile$ = autoplayPoolFile$ apf = invalid currentSync = invalid endif return schedule End Function Function newSchedule(autoScheduleXML As Object) As Object schedule = CreateObject("roAssociativeArray") ' create and read schedules scheduledPresentationsXML = autoScheduleXML.scheduledPresentation numScheduledPresentations% = scheduledPresentationsXML.Count() schedule.allScheduledEvents = CreateObject("roArray", numScheduledPresentations%, true) schedule.scheduledEvents = CreateObject("roArray", numScheduledPresentations%, true) schedule.scheduledInterruptions = CreateObject("roArray", 1, true) for each scheduledPresentationXML in scheduledPresentationsXML scheduledPresentationBS = newScheduledEvent(scheduledPresentationXML) schedule.allScheduledEvents.push(scheduledPresentationBS) if scheduledPresentationBS.interruption then schedule.scheduledInterruptions.push(scheduledPresentationBS) else schedule.scheduledEvents.push(scheduledPresentationBS) endif next ' get starting presentation schedule.GetActiveScheduledEvent = GetActiveScheduledEvent schedule.GetNextScheduledEventTime = GetNextScheduledEventTime schedule.activeScheduledEvent = schedule.GetActiveScheduledEvent(schedule.scheduledInterruptions) if type(schedule.activeScheduledEvent) <> "roAssociativeArray" then schedule.activeScheduledEvent = schedule.GetActiveScheduledEvent(schedule.scheduledEvents) endif if type(schedule.activeScheduledEvent) <> "roAssociativeArray" then schedule.nextScheduledEventTime = schedule.GetNextScheduledEventTime(schedule.allScheduledEvents) else ' determine when this scheduled event will end schedule.activeScheduledEventEndDateTime = invalid if schedule.activeScheduledEvent.interruption then schedule.activeScheduledEventEndDateTime = CopyDateTime(schedule.activeScheduledEvent.dateTime) schedule.activeScheduledEventEndDateTime.AddSeconds(schedule.activeScheduledEvent.duration% * 60) else endDateTime = invalid if not schedule.activeScheduledEvent.allDayEveryDay then endDateTime = CopyDateTime(schedule.activeScheduledEvent.dateTime) endDateTime.AddSeconds(schedule.activeScheduledEvent.duration% * 60) endif nextInterruptionStartTime = schedule.GetNextScheduledEventTime(schedule.scheduledInterruptions) if endDateTime = invalid then if nextInterruptionStartTime <> invalid then schedule.activeScheduledEventEndDateTime = nextInterruptionStartTime endif else if nextInterruptionStartTime = invalid then schedule.activeScheduledEventEndDateTime = endDateTime else if endDateTime.GetString() < nextInterruptionStartTime.GetString() then schedule.activeScheduledEventEndDateTime = endDateTime else schedule.activeScheduledEventEndDateTime = nextInterruptionStartTime endif endif endif endif endif return schedule End Function Function newScheduledEvent(scheduledEventXML As Object) As Object scheduledEventBS = CreateObject("roAssociativeArray") if scheduledEventXML.playlist.Count() > 0 then scheduledEventBS.playlist$ = scheduledEventXML.playlist.GetText() endif if scheduledEventXML.presentationToSchedule.Count() > 0 then scheduledEventBS.presentationName$ = scheduledEventXML.presentationToSchedule.name.GetText() endif dateTime$ = scheduledEventXML.dateTime.GetText() scheduledEventBS.dateTime = FixDateTime(dateTime$) scheduledEventBS.duration% = int(val(scheduledEventXML.duration.GetText())) if lcase(scheduledEventXML.allDayEveryDay.GetText()) = "true" then scheduledEventBS.allDayEveryDay = true else scheduledEventBS.allDayEveryDay = false endif if lcase(scheduledEventXML.recurrence.GetText()) = "true" then scheduledEventBS.recurrence = true else scheduledEventBS.recurrence = false endif scheduledEventBS.recurrencePattern$ = scheduledEventXML.recurrencePattern.GetText() scheduledEventBS.recurrencePatternDaily$ = scheduledEventXML.recurrencePatternDaily.GetText() scheduledEventBS.recurrencePatternDaysOfWeek% = int(val(scheduledEventXML.recurrencePatternDaysOfWeek.GetText())) dateTime$ = scheduledEventXML.recurrenceStartDate.GetText() scheduledEventBS.recurrenceStartDate = FixDateTime(dateTime$) if lcase(scheduledEventXML.recurrenceGoesForever.GetText()) = "true" then scheduledEventBS.recurrenceGoesForever = true else scheduledEventBS.recurrenceGoesForever = false endif dateTime$ = scheduledEventXML.recurrenceEndDate.GetText() recurrenceEndDate = FixDateTime(dateTime$) recurrenceEndDate.AddSeconds(60 * 60 * 24) ' adjust the recurrence end date to refer to the beginning of the next day scheduledEventBS.recurrenceEndDate = recurrenceEndDate if scheduledEventXML.interruption.GetText() <> "" and lcase(scheduledEventXML.interruption.GetText()) = "true" then scheduledEventBS.interruption = true else scheduledEventBS.interruption = false endif return scheduledEventBS End Function ' required format looks like ' 2012-10-03T15:49:00 Function FixDateTime(dateTime$ As String) As Object dateTime = CreateObject("roDateTime") ' strip '-' and ':' so that BrightSign can parse the dateTime properly index = instr(1, dateTime$, "-") while index > 0 a$ = mid(dateTime$, 1, index - 1) b$ = mid(dateTime$, index + 1) dateTime$ = a$ + b$ index = instr(1, dateTime$, "-") end while index = instr(1, dateTime$, ":") while index > 0 a$ = mid(dateTime$, 1, index - 1) b$ = mid(dateTime$, index + 1) dateTime$ = a$ + b$ index = instr(1, dateTime$, ":") end while if not dateTime.FromIsoString(dateTime$) then return Invalid endif return dateTime End Function Function GetActiveScheduledEvent(scheduledEvents As Object) As Object ' determine if there is a scheduled event that should be active at this time activeScheduledEvent = invalid for each scheduledEvent in scheduledEvents ' is there a playlist that should be active now based on the scheduledEvent? if scheduledEvent.allDayEveryDay then activeScheduledEvent = scheduledEvent exit for endif ' is the current scheduledEvent active today? if no, go to next scheduledEvent eventDateTime = scheduledEvent.dateTime systemTime = CreateObject("roSystemTime") currentDateTime = systemTime.GetLocalDateTime() scheduledEventActiveToday = false ' if it's not a recurring event and its start date is today, then it is active today if not scheduledEvent.recurrence then if eventDateTime.GetYear() = currentDateTime.GetYear() and eventDateTime.GetMonth() = currentDateTime.GetMonth() and eventDateTime.GetDay() = currentDateTime.GetDay() then scheduledEventActiveToday = true endif endif if (not scheduledEventActiveToday) and scheduledEvent.recurrence then ' determine if the date represented by the scheduled event is within the recurrence range dateWithinRange = false if scheduledEvent.recurrenceStartDate.GetString() < currentDateTime.GetString() then if scheduledEvent.recurrenceGoesForever then dateWithinRange = true else if scheduledEvent.recurrenceEndDate.GetString() >= currentDateTime.GetString() then dateWithinRange = true endif endif ' if it is within the range, check the recurrence pattern if dateWithinRange then if scheduledEvent.recurrencePattern$ = "Daily" then if scheduledEvent.recurrencePatternDaily$ = "EveryDay" then scheduledEventActiveToday = true else if scheduledEvent.recurrencePatternDaily$ = "EveryWeekday" then if currentDateTime.GetDayOfWeek() > 0 and currentDateTime.GetDayOfWeek() < 6 then scheduledEventActiveToday = true endif else ' EveryWeekend if currentDateTime.GetDayOfWeek() = 0 or currentDateTime.GetDayOfWeek() = 6 then scheduledEventActiveToday = true endif endif else ' Weekly bitwiseDaysOfWeek% = scheduledEvent.recurrencePatternDaysOfWeek% currentDayOfWeek = currentDateTime.GetDayOfWeek() bitDayOfWeek% = 2 ^ currentDayOfWeek if (bitwiseDaysOfWeek% and bitDayOfWeek%) <> 0 then scheduledEventActiveToday = true endif endif endif endif ' see if the currentScheduledEvent should be active right now ' it will be active right now if its start time < current start time and its end time > current start time if scheduledEventActiveToday then eventTodayStartTime = systemTime.GetLocalDateTime() eventTodayStartTime.SetHour(scheduledEvent.dateTime.GetHour()) eventTodayStartTime.SetMinute(scheduledEvent.dateTime.GetMinute()) eventTodayStartTime.SetSecond(scheduledEvent.dateTime.GetSecond()) eventTodayStartTime.SetMillisecond(0) eventTodayEndTime = systemTime.GetLocalDateTime() eventTodayEndTime.SetHour(scheduledEvent.dateTime.GetHour()) eventTodayEndTime.SetMinute(scheduledEvent.dateTime.GetMinute()) eventTodayEndTime.SetSecond(scheduledEvent.dateTime.GetSecond()) eventTodayEndTime.SetMillisecond(0) eventTodayEndTime.AddSeconds(scheduledEvent.duration% * 60) if eventTodayStartTime.GetString() <= currentDateTime.GetString() and eventTodayEndTime.GetString() > currentDateTime.GetString() then activeScheduledEvent = scheduledEvent activeScheduledEvent.dateTime = eventTodayStartTime ' print "at end, eventDateTime = ";eventDateTime.GetString() ' print "at end, currentDateTime = ";currentDateTime.GetString() exit for endif endif next return activeScheduledEvent End Function Function GetNextScheduledEventTime(scheduledEvents) As Object dim futureScheduledEvents[10] dim futureScheduledEventStartTimes[10] nextScheduledEventTime = CreateObject("roDateTime") ' for each scheduled event, see if it could start in the future. If yes, determine the earliest ' future start time that is later than now. Store the scheduled event and that start time. ' Use the scheduled event in that list with the lowest start time. for each scheduledEvent in scheduledEvents if scheduledEvent.allDayEveryDay then ' an allDayEveryDay event is always active, so by definition, it is not a future event. goto endLoop endif eventDateTime = scheduledEvent.dateTime systemTime = CreateObject("roSystemTime") currentDateTime = systemTime.GetLocalDateTime() ' if it's not a recurring event and its start date/time is in the future, then it is eligible if not scheduledEvent.recurrence then if eventDateTime.GetString() > currentDateTime.GetString() then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventDateTime) goto endLoop endif endif ' if it's a recurring event, see if its date range includes the future if scheduledEvent.recurrence then eventToday = CreateObject("roDateTime") eventToday.SetYear(currentDateTime.GetYear()) eventToday.SetMonth(currentDateTime.GetMonth()) eventToday.SetDay(currentDateTime.GetDay()) eventToday.SetHour(eventDateTime.GetHour()) eventToday.SetMinute(eventDateTime.GetMinute()) if scheduledEvent.recurrenceGoesForever or scheduledEvent.recurrenceEndDate.GetString() > currentDateTime.GetString() then ' find the earliest time > now that this recurring event could start if scheduledEvent.recurrencePattern$ = "Daily" then if scheduledEvent.recurrencePatternDaily$ = "EveryDay" then if eventToday.GetString() > currentDateTime.GetString() then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop else ' use the next day eventToday.AddSeconds(60 * 60 * 24) futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif else if scheduledEvent.recurrencePatternDaily$ = "EveryWeekday" then ' if today is a weekday, proceed as in the case above, except that instead of using ' the 'next day', use the 'next weekday' (which may or may not be the next day) for the test if currentDateTime.GetDayOfWeek() > 0 and currentDateTime.GetDayOfWeek() < 6 then ' current day is a weekday if eventToday.GetString() > currentDateTime.GetString() then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop else ' if today is Friday, add 3 days daysToAdd% = 1 if currentDateTime.GetDayOfWeek() = 5 then daysToAdd% = 3 eventToday.AddSeconds(60 * 60 * 24 * daysToAdd%) futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif else ' current day is a weekend ' if today is not a weekday, the next weekday (Monday) is the future event daysToAdd% = 1 if currentDateTime.GetDayOfWeek() = 6 then daysToAdd% = 2 eventToday.AddSeconds(60 * 60 * 24 * daysToAdd%) futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif else ' EveryWeekend ' if today is a weekend, proceed as in the case above, except that instead of using ' the 'next day', use the 'next weekend' (which may or may not be the next day) for the test if currentDateTime.GetDayOfWeek() = 0 or currentDateTime.GetDayOfWeek() = 6 then ' current day is a weekend if eventToday.GetString() > currentDateTime.GetString() then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop else ' if today is Sunday, add 6 days daysToAdd% = 1 if currentDateTime.GetDayOfWeek() = 5 then daysToAdd% = 6 eventToday.AddSeconds(60 * 60 * 24 * daysToAdd%) futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif else ' current day is a weekday ' if today is not a weekday, the next weekday (Monday) is the future event daysToAdd% = 6 - currentDateTime.GetDayOfWeek() eventToday.AddSeconds(60 * 60 * 24 * daysToAdd%) futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif endif else ' Weekly ' if today is one of the days specified, test against today. if the test fails, ' or today is not one of the days specified, find the next specified day and use it. bitwiseDaysOfWeek% = scheduledEvent.recurrencePatternDaysOfWeek% currentDayOfWeek = currentDateTime.GetDayOfWeek() bitDayOfWeek% = 2 ^ currentDayOfWeek if (bitwiseDaysOfWeek% and bitDayOfWeek%) <> 0 then if eventToday.GetString() > currentDateTime.GetString() then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif endif ' find the next specified day and use it if bitwiseDaysOfWeek% <> 0 then while true currentDayOfWeek = currentDayOfWeek + 1 if currentDayOfWeek >= 7 then currentDayOfWeek = 0 bitDayOfWeek% = 2 ^ currentDayOfWeek eventToday.AddSeconds(60 * 60 * 24) if (bitwiseDaysOfWeek% and bitDayOfWeek%) <> 0 then futureScheduledEvents.push(scheduledEvent) futureScheduledEventStartTimes.push(eventToday) goto endLoop endif end while endif endif endif endif endLoop: next ' sort the future events dim sortedFutureEventTimes[10] if futureScheduledEventStartTimes.Count() > 1 then SortFutureScheduledEvents(futureScheduledEventStartTimes, sortedFutureEventTimes) nextScheduledEventTime = futureScheduledEventStartTimes[sortedFutureEventTimes[0]] else nextScheduledEventTime = futureScheduledEventStartTimes[0] endif return nextScheduledEventTime End Function Sub SortFutureScheduledEvents(futureEventTimes As Object, sortedIndices As Object) ' initialize array with indices. for i% = 0 to futureEventTimes.Count()-1 sortedIndices[i%] = i% next numItemsToSort% = futureEventTimes.Count() for i% = numItemsToSort% - 1 to 1 step -1 for j% = 0 to i%-1 index0 = sortedIndices[j%] time0 = futureEventTimes[index0].GetString() index1 = sortedIndices[j%+1] time1 = futureEventTimes[index1].GetString() if time0 > time1 then k% = sortedIndices[j%] sortedIndices[j%] = sortedIndices[j%+1] sortedIndices[j%+1] = k% endif next next return End Sub 'endregion 'region StartPlayback and CreateObjects Sub StartPlayback() sign = m.sign ' set a default udp receive port m.udpReceivePort = sign.udpReceivePort m.udpSendPort = sign.udpSendPort m.udpAddress$ = sign.udpAddress$ m.udpAddressType$ = sign.udpAddressType$ EnableZoneSupport(true) ' kick off playback m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("### set background screen color") ' set background screen color if type(sign.backgroundScreenColor%) = "roInt" then videoMode = CreateObject("roVideoMode") videoMode.SetBackgroundColor(sign.backgroundScreenColor%) videoMode = invalid endif ' unmute all audio explicitly for Cheetah / Panther / Puma m.UnmuteAllAudio() numZones% = sign.zonesHSM.Count() if numZones% > 0 then ' construct zones for i% = 0 to numZones% - 1 zoneHSM = sign.zonesHSM[i%] if type(zoneHSM.playlist) = "roAssociativeArray" then m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("### Constructor zone") zoneHSM.Constructor() endif next ' launch the zones for i% = 0 to numZones% - 1 zoneHSM = sign.zonesHSM[i%] if type(zoneHSM.playlist) = "roAssociativeArray" then m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("### Launch playback") zoneHSM.Initialize() endif next endif ' Notify controlling devices we have started playback m.SendUDPNotification("startPlayback") End Sub ' m is the zone Sub CreateObjects() zoneHSM = m ' is there any harm in creating a keyboard object even if it is not used? if type(m.bsp.keyboard) <> "roKeyboard" then m.bsp.keyboard = CreateObject("roKeyboard") m.bsp.keyboard.SetPort(m.bsp.msgPort) endif for each key in zoneHSM.stateTable state = zoneHSM.stateTable[key] if state.type$ = "tripleUSB" then m.CreateSerial(m.bsp, state.tripleUSBPort$, false) if IsString(state.boseProductPort$) and state.boseProductPort$ <> "" then m.CreateSerial(m.bsp, state.boseProductPort$, true) endif endif if state.type$ = "xModem" then m.CreateSerial(m.bsp, state.port$, false) endif gpioEvents = state.gpioEvents for each gpioEventNumber in gpioEvents if type(gpioEvents[gpioEventNumber]) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(gpioEvents[gpioEventNumber]) endif next gpioUpEvents = state.gpioUpEvents for each gpioEventNumber in gpioUpEvents if type(gpioUpEvents[gpioEventNumber]) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(gpioUpEvents[gpioEventNumber]) endif next for buttonPanelIndex% = 0 to 2 bpEvents = state.bpEvents[buttonPanelIndex%] for each bpEventNumber in bpEvents if type(bpEvents[bpEventNumber]) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(bpEvents[bpEventNumber]) endif next next if type(state.mstimeoutEvent) = "roAssociativeArray" m.CreateObjectsNeededForTransitionCommands(state.mstimeoutEvent) endif if type(state.timeClockEvents) = "roArray" then for each timeClockEvent in state.timeClockEvents m.CreateObjectsNeededForTransitionCommands(timeClockEvent.transition) next endif if type(state.videoEndEvent) = "roAssociativeArray" m.CreateObjectsNeededForTransitionCommands(state.videoEndEvent) endif if type(state.audioEndEvent) = "roAssociativeArray" m.CreateObjectsNeededForTransitionCommands(state.audioEndEvent) endif if type(state.keyboardEvents) = "roAssociativeArray" or type(state.usbStringEvents) = "roAssociativeArray" then if type(state.keyboardEvents) = "roAssociativeArray" then keyboardEvents = state.keyboardEvents for each keyboardEvent in state.keyboardEvents if type(keyboardEvents[keyboardEvent]) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(keyboardEvents[keyboardEvent]) endif next endif if type(state.usbStringEvents) = "roAssociativeArray" usbEvents = state.usbStringEvents for each usbEvent in state.usbEvents if type(usbEvents[usbEvent]) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(usbEvents[usbEvent]) endif next endif endif if type(state.remoteEvents) = "roAssociativeArray" then if type(m.bsp.remote) <> "roIRRemote" then m.bsp.remote = CreateObject("roIRRemote") m.bsp.remote.SetPort(m.bsp.msgPort) endif remoteEvents = state.remoteEvents for each remoteEvent in state.remoteEvents m.CreateObjectsNeededForTransitionCommands(remoteEvents[remoteEvent]) next endif if type(state.usbBinaryEtapEvents) = "roArray" then if type(m.usbBinaryEtap) <> "roUsbBinaryEtap" then m.bsp.usbBinaryEtap = CreateObject("roUsbBinaryEtap", 0) m.bsp.usbBinaryEtap.SetPort(m.bsp.msgPort) endif for each usbBinaryEtapInputTransitionSpec in state.usbBinaryEtapEvents m.CreateObjectsNeededForTransitionCommands(usbBinaryEtapInputTransitionSpec.transition) next endif if (type(state.gpsEnterRegionEvents) = "roArray" and state.gpsEnterRegionEvents.Count() > 0) or (type(state.gpsExitRegionEvents) = "roArray" and state.gpsExitRegionEvents.Count() > 0) then m.CreateSerial(m.bsp, m.bsp.gpsPort$, false) endif serialEvents = state.serialEvents for each serialPort in serialEvents m.CreateSerial(m.bsp, serialPort, false) port% = int(val(serialPort)) serialPortConfiguration = m.bsp.sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ if protocol$ = "Binary" then if type(serialEvents[serialPort]) = "roAssociativeArray" then if type(serialEvents[serialPort].streamInputTransitionSpecs) = "roArray" then for each streamInputTransitionSpec in serialEvents[serialPort].streamInputTransitionSpecs m.CreateObjectsNeededForTransitionCommands(streamInputTransitionSpec.transition) next endif endif else for each serialEvent in serialEvents[serialPort] m.CreateObjectsNeededForTransitionCommands(serialEvents[serialPort][serialEvent]) next endif next if type(state.zoneMessageEvents) = "roAssociativeArray" then for each zoneMessageEvent in state.zoneMessageEvents m.CreateObjectsNeededForTransitionCommands(state.zoneMessageEvents[zoneMessageEvent]) next endif if type(state.pluginMessageEvents) = "roAssociativeArray" then for each pluginMessageEvent in state.pluginMessageEvents m.CreateObjectsNeededForTransitionCommands(state.pluginMessageEvents[pluginMessageEvent]) next endif if type(state.internalSynchronizeEvents) = "roAssociativeArray" then for each internalSynchronizeEvent in state.internalSynchronizeEvents m.CreateObjectsNeededForTransitionCommands(state.internalSynchronizeEvents[internalSynchronizeEvent]) next endif createDatagramReceiver = false if state.type$ = "rfInputChannel" then if type(state.channelUpEvent) = "roAssociativeArray" and IsString(state.channelUpEvent.udpUserEvent$) createDatagramReceiver = true if type(state.channelDownEvent) = "roAssociativeArray" and IsString(state.channelDownEvent.udpUserEvent$) createDatagramReceiver = true endif if type(state.udpEvents) = "roAssociativeArray" or type(state.synchronizeEvents) = "roAssociativeArray" or createDatagramReceiver then createDatagramReceiver = false if type(m.bsp.udpReceiver) <> "roDatagramReceiver" then createDatagramReceiver = true else if type(m.bsp.existingUdpReceivePort) = "roInt" and m.bsp.existingUdpReceivePort <> m.bsp.udpReceivePort then createDatagramReceiver = true endif endif if createDatagramReceiver then m.bsp.udpReceiver = CreateObject("roDatagramReceiver", m.bsp.udpReceivePort) m.bsp.udpReceiver.SetPort(m.bsp.msgPort) m.bsp.existingUdpReceivePort = m.bsp.udpReceivePort endif if type(state.udpEvents) = "roAssociativeArray" then udpEvents = state.udpEvents for each udpEvent in state.udpEvents m.CreateObjectsNeededForTransitionCommands(udpEvents[udpEvent]) next endif if type(state.synchronizeEvents) = "roAssociativeArray" then synchronizeEvents = state.synchronizeEvents for each synchronizeEvent in state.synchronizeEvents m.CreateObjectsNeededForTransitionCommands(synchronizeEvents[synchronizeEvent]) next endif endif if state.type$ = "html5" and state.enableMouseEvents then m.bsp.InitializeTouchScreen(zoneHSM) endif if type(state.touchEvents) = "roAssociativeArray" then m.bsp.InitializeTouchScreen(zoneHSM) for each eventNum in state.touchEvents m.bsp.AddRectangularTouchRegion(m, state.touchEvents[eventNum], val(eventNum)) m.CreateObjectsNeededForTransitionCommands(state.touchEvents[eventNum]) next endif if type(state.videoTimeCodeEvents) = "roAssociativeArray" then for each eventNum in state.videoTimeCodeEvents m.CreateObjectsNeededForTransitionCommands(state.videoTimeCodeEvents[eventNum]) next endif if type(state.cmds) = "roArray" then for each cmd in state.cmds m.CreateCommunicationObjects(cmd) next endif if type(state.exitCmds) = "roArray" then for each cmd in state.exitCmds m.CreateCommunicationObjects(cmd) next endif next if m.bsp.sign.enableEnhancedSynchronization and m.bsp.sysInfo.modelSupportsEnhancedSynchronization and (m.bsp.IsSyncMaster or m.bsp.IsSyncSlave) then aa = {} aa.Domain = m.bsp.sign.ptpDomain$ m.bsp.SyncManager = invalid m.bsp.SyncManager = CreateObject("roSyncManager", aa) m.bsp.SyncManager.SetMasterMode(m.bsp.IsSyncMaster) if m.bsp.IsSyncSlave then m.bsp.SyncManager.SetPort(m.bsp.msgPort) endif endif End Sub Sub CreateCommunicationObjects(cmd As Object) commandName$ = cmd.name$ if commandName$ = "sendUDPCommand" or commandName$ = "synchronize" then m.CreateUDPSender(m.bsp) else if commandName$ = "sendSerialStringCommand" or commandName$ = "sendSerialBlockCommand" or commandName$ = "sendSerialByteCommand" or commandName$ = "sendSerialBytesCommand" then port$ = cmd.parameters["port"].GetCurrentParameterValue() m.CreateSerial(m.bsp, port$, true) endif End Sub Sub CreateObjectsNeededForTransitionCommands(transition As Object) if type(transition.transitionCmds) = "roArray" then for each cmd in transition.transitionCmds m.CreateObjectForTransitionCommand(cmd) next endif if type(transition.conditionalTargets) = "roArray" then for each conditionalTarget in transition.conditionalTargets if type(conditionalTarget.transitionCmds) = "roArray" then for each cmd in conditionalTarget.transitionCmds m.CreateObjectForTransitionCommand(cmd) next endif next endif End Sub Sub CreateObjectForTransitionCommand(cmd As Object) commandName$ = cmd.name$ if commandName$ = "sendUDPCommand" or commandName$ = "synchronize" then m.CreateUDPSender(m.bsp) else if commandName$ = "sendSerialStringCommand" or commandName$ = "sendSerialBlockCommand" or commandName$ = "sendSerialByteCommand" or commandName$ = "sendSerialBytesCommand" then port$ = cmd.parameters["port"].GetCurrentParameterValue() m.CreateSerial(m.bsp, port$, true) endif if commandName$ = "synchronize" then m.bsp.IsSyncMaster = true endif End Sub Sub ConfigureBPs() m.bpOutput = CreateObject("roArray", 3, true) m.bpOutputSetup = CreateObject("roArray", 3, true) m.ConfigureBP(0, "TouchBoard-0-LED", "TouchBoard-0-LED-SETUP") m.ConfigureBP(1, "TouchBoard-1-LED", "TouchBoard-1-LED-SETUP") m.ConfigureBP(2, "TouchBoard-2-LED", "TouchBoard-2-LED-SETUP") End Sub Sub ConfigureBP(buttonPanelIndex% As Integer, touchBoardLED$ As String, touchBoardLEDSetup$ As String) if type(m.bpInputPorts[buttonPanelIndex%]) = "roControlPort" then if type(m.bpOutput[buttonPanelIndex%]) <> "roControlPort" then m.diagnostics.PrintDebug("Creating bpOutput") m.bpOutput[buttonPanelIndex%] = CreateObject("roControlPort", touchBoardLED$) if type(m.bpOutput[buttonPanelIndex%]) = "roControlPort" then m.bpOutputSetup[buttonPanelIndex%] = CreateObject("roControlPort", touchBoardLEDSetup$) if type(m.bpOutputSetup[buttonPanelIndex%]) = "roControlPort" then m.bpOutputSetup[buttonPanelIndex%].SetOutputValue(0, 22) endif endif endif endif End Sub Sub CreateUDPSender(bsp As Object) createDatagramSender = false if type(bsp.udpSender) <> "roDatagramSender" then createDatagramSender = true else if (type(bsp.existingUdpAddressType$) = "roString" and bsp.existingUdpAddressType$ <> bsp.udpAddressType$) or (type(bsp.existingUdpAddress$) = "roString" and bsp.existingUdpAddress$ <> bsp.udpAddress$) or (type(bsp.existingUdpSendPort) = "roInt" and bsp.existingUdpSendPort <> bsp.udpSendPort) then createDatagramSender = true endif endif if createDatagramSender then bsp.diagnostics.PrintDebug("Creating roDatagramSender") bsp.udpSender = CreateObject("roDatagramSender") if bsp.udpAddressType$ = "LocalSubnet" then bsp.udpSender.SetDestination("BCAST-LOCAL-SUBNETS", bsp.udpSendPort) else bsp.udpSender.SetDestination(bsp.udpAddress$, bsp.udpSendPort) endif bsp.existingUdpAddressType$ = bsp.udpAddressType$ bsp.existingUdpAddress$ = bsp.udpAddress$ bsp.existingUdpSendPort = bsp.udpSendPort endif End Sub Sub SendUDPNotification(msg As String) if m.registrySettings.lwsEnableUpdateNotifications$ <> "yes" then return endif if type(m.udpNotifier) <> "roDatagramSender" then m.diagnostics.PrintDebug("Creating roDatagramSender for notifications") m.udpNotifier = CreateObject("roDatagramSender") m.udpNotifier.SetDestination(m.udpNotificationAddress$, m.udpNotificationPort%) endif m.udpNotifier.Send(msg) m.diagnostics.PrintDebug("UDP notification sent: " + msg) End Sub Sub CreateSerial(bsp As Object, port$ As String, outputOnly As Boolean) if type(bsp.serial) <> "roAssociativeArray" then bsp.serial = CreateObject("roAssociativeArray") endif port% = int(val(port$)) serialPortConfiguration = bsp.serialPortConfigurations[port%] serialPortSpeed% = serialPortConfiguration.serialPortSpeed% serialPortMode$ = serialPortConfiguration.serialPortMode$ if type(bsp.serial[port$]) = "roSerialPort" then serial = bsp.serial[port$] else serial = CreateObject("roSerialPort", port%, serialPortSpeed%) if type(serial) <> "roSerialPort" then bsp.diagnostics.PrintDebug("Error creating roSerialPort " + port$) return endif endif ok = serial.SetMode(serialPortMode$) if not ok then print "Error setting serial mode" : stop protocol$ = serialPortConfiguration.protocol$ sendEol$ = serialPortConfiguration.sendEol$ receiveEol$ = serialPortConfiguration.receiveEol$ serial.SetSendEol(sendEol$) serial.SetReceiveEol(receiveEol$) if serialPortConfiguration.invertSignals then serial.SetInverted(1) else serial.SetInverted(0) endif serial.SetUserData(port$) bsp.serial[port$] = serial if not outputOnly then if protocol$ = "Binary" then serial.SetByteEventPort(m.msgPort) else serial.SetLineEventPort(m.msgPort) endif endif End Sub Function IsString(inputVariable As Object) As Boolean if type(inputVariable) = "roString" or type(inputVariable) = "String" then return true return false End Function Function IsInteger(inputVariable As Object) As Boolean if type(inputVariable) = "roInt" or type(inputVariable) = "Integer" then return true return false End Function Function GetEolFromSpec(eolSpec$ As String) As String eol$ = chr(13) if eolSpec$ = "LF" then eol$ = chr(10) else if eolSpec$ = "CR+LF" then eol$ = chr(13) + chr(10) endif return eol$ End Function 'endregion 'region newSign Function newSign(BrightAuthor As Object, globalVariables As Object, bsp As Object, msgPort As Object, controlPort As Object, version% As Integer) As Object ' check for compatibility - Panther / Cheetah presentations won't play on other families publishedModel$ = BrightAuthor.meta.model.GetText() if publishedModel$ = "XD230" or publishedModel$ = "XD1030" or publishedModel$ = "XD1230" or publishedModel$ = "HD120" or publishedModel$ = "HD220" or publishedModel$ = "HD1020" or publishedModel$ = "AU320" then if bsp.sysInfo.deviceFamily$ = "monaco" or bsp.sysInfo.deviceFamily$ = "pandora3" or bsp.sysInfo.deviceFamily$ = "apollo" or bsp.sysInfo.deviceFamily$ = "bpollo" then DisplayIncompatibleModelMessage(publishedModel$, bsp.sysInfo.deviceModel$) endif endif ' determine whether this presentation could use new or old parameters (a Panther might use either) bsp.currentPresentationUsesRoAudioOutputParameters = true if publishedModel$ = "HD110" or publishedModel$ = "HD210" or publishedModel$ = "HD410" or publishedModel$ = "HD810" or publishedModel$ = "HD910" or publishedModel$ = "HD912" or publishedModel$ = "A913" or publishedModel$ = "A933" or publishedModel$ = "HD960" or publishedModel$ = "HD962" or publishedModel$ = "TD1012" or publishedModel$ = "HD1010" then bsp.currentPresentationUsesRoAudioOutputParameters = false endif ' reset variable bsp.mediaListInactivity = invalid Sign = CreateObject("roAssociativeArray") Sign.numTouchEvents% = 0 Sign.numVideoTimeCodeEvents% = 0 ' get sign data Sign.name$ = BrightAuthor.meta.name.GetText() if not IsString(Sign.name$) then print "Invalid XML file - meta name not found" : stop videoMode = CreateObject("roVideoMode") Sign.videoMode$ = BrightAuthor.meta.videoMode.GetText() if Sign.videoMode$ <> "not applicable" then if not IsString(Sign.videoMode$) then print "Invalid XML file - meta videoMode not found" : stop ' print "Video mode is ";Sign.videoMode$ ok = videoMode.SetMode(Sign.videoMode$) if ok = 0 then print "Error: Can't set VIDEOMODE to ::"; Sign.videoMode$; " resetting to 1920x1080x60i" ok = videoMode.SetMode("1920x1080x60i") endif Sign.videoConnector$ = BrightAuthor.meta.videoConnector.GetText() if not IsString(Sign.videoConnector$) then print "Invalid XML file - meta videoConnector not found" : stop ' print "Video connector is ";Sign.videoConnector$ endif Sign.isVideoWall = false if BrightAuthor.meta.stretchedVideoWall.Count() = 1 then Sign.isVideoWall = true Sign.videoWallType$ = "stretched" Sign.videoWallNumRows% = int(val(BrightAuthor.meta.stretchedVideoWall.videoWallNumRows.GetText())) Sign.videoWallNumColumns% = int(val(BrightAuthor.meta.stretchedVideoWall.videoWallNumColumns.GetText())) Sign.videoWallRowPosition% = int(val(BrightAuthor.meta.stretchedVideoWall.videoWallRowPosition.GetText())) Sign.videoWallColumnPosition% = int(val(BrightAuthor.meta.stretchedVideoWall.videoWallColumnPosition.GetText())) videoMode.SetMultiscreenBezel(int(val(BrightAuthor.meta.stretchedVideoWall.bezelWidthPercentage.GetText())), int(val(BrightAuthor.meta.stretchedVideoWall.bezelHeightPercentage.GetText()))) else videoMode.SetMultiscreenBezel(0, 0) endif videoMode = invalid Sign.monitorOrientation = "landscape" if BrightAuthor.meta.monitorOrientation.GetText() <> "" then Sign.monitorOrientation = lcase(BrightAuthor.meta.monitorOrientation.GetText()) endif Sign.timezone$ = BrightAuthor.meta.timezone.GetText() if IsString(Sign.timezone$) then bsSystemTime = CreateObject("roSystemTime") if Sign.timezone$ <> "" then bsSystemTime.SetTimeZone(Sign.timezone$) endif bsSystemTime = 0 endif rssDownloadSpec = BrightAuthor.meta.rssDownloadSpec Sign.rssDownloadPeriodicValue% = GetRSSDownloadInterval(rssDownloadSpec) Sign.deviceWebPageDisplay$ = "Standard" if BrightAuthor.meta.deviceWebPageDisplay.GetText() <> "" then Sign.deviceWebPageDisplay$ = BrightAuthor.meta.deviceWebPageDisplay.GetText() endif Sign.customDeviceWebPage$ = BrightAuthor.meta.customDeviceWebPage.GetText() Sign.alphabetizeVariableNames = true if BrightAuthor.meta.alphabetizeVariableNames.GetText() <> "" and lcase(BrightAuthor.meta.alphabetizeVariableNames.GetText()) = "false" then Sign.alphabetizeVariableNames = false endif Sign.htmlEnableJavascriptConsole = false if BrightAuthor.meta.htmlEnableJavascriptConsole.GetText() <> "" and lcase(BrightAuthor.meta.htmlEnableJavascriptConsole.GetText()) = "true" then Sign.htmlEnableJavascriptConsole = true endif backgroundScreenColor = BrightAuthor.meta.backgroundScreenColor Sign.backgroundScreenColor% = GetColor(backgroundScreenColor.GetAttributes()) bsp.dontChangePresentationUntilMediaEndEventReceived = false delay$ = BrightAuthor.meta.delayScheduleChangeUntilMediaEndEvent.GetText() if delay$ <> "" and lcase(delay$) = "true" then bsp.dontChangePresentationUntilMediaEndEventReceived = true endif Sign.languageKey$ = BrightAuthor.meta.languageKey.GetText() globalVariables.language$ = Sign.languageKey$ Sign.serialPortConfigurations = CreateObject("roArray", 6, true) bp900AConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900AConfigureAutomatically.GetText(), true) bp900BConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900BConfigureAutomatically.GetText(), true) bp900CConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900CConfigureAutomatically.GetText(), true) bp200AConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200AConfigureAutomatically.GetText(), true) bp200BConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200BConfigureAutomatically.GetText(), true) bp200CConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200CConfigureAutomatically.GetText(), true) bp900AConfiguration% = GetIntFromString(BrightAuthor.meta.BP900AConfiguration.GetText()) bp900BConfiguration% = GetIntFromString(BrightAuthor.meta.BP900BConfiguration.GetText()) bp900CConfiguration% = GetIntFromString(BrightAuthor.meta.BP900CConfiguration.GetText()) bp200AConfiguration% = GetIntFromString(BrightAuthor.meta.BP200AConfiguration.GetText()) bp200BConfiguration% = GetIntFromString(BrightAuthor.meta.BP200BConfiguration.GetText()) bp200CConfiguration% = GetIntFromString(BrightAuthor.meta.BP200CConfiguration.GetText()) if type(bsp.bpInputPorts[0]) = "roControlPort" then bsp.bpInputPortConfigurations[0] = GetBPConfiguration(bsp.bpInputPortHardware[0], bp900AConfigureAutomatically, bp900AConfiguration%, bp200AConfigureAutomatically, bp200AConfiguration%) endif if type(bsp.bpInputPorts[1]) = "roControlPort" then bsp.bpInputPortConfigurations[1] = GetBPConfiguration(bsp.bpInputPortHardware[1], bp900BConfigureAutomatically, bp900BConfiguration%, bp200BConfigureAutomatically, bp200BConfiguration%) endif if type(bsp.bpInputPorts[2]) = "roControlPort" then bsp.bpInputPortConfigurations[2] = GetBPConfiguration(bsp.bpInputPortHardware[2], bp900CConfigureAutomatically, bp900CConfiguration%, bp200CConfigureAutomatically, bp200CConfiguration%) endif bsp.gpsConfigured = false bsp.gpsLocation = { latitude: invalid, longitude: invalid } bsp.gpsPort$ = "" if BrightAuthor.meta.baudRate.Count() = 1 then ' old format serialPortSpeed% = int(val(BrightAuthor.meta.baudRate.GetText())) serialPortMode$ = BrightAuthor.meta.dataBits.GetText() + BrightAuthor.meta.parity.GetText() + BrightAuthor.meta.stopBits.GetText() serialPortConfiguration = CreateObject("roAssociativeArray") serialPortConfiguration.serialPortSpeed% = serialPortSpeed% serialPortConfiguration.serialPortMode$ = serialPortMode$ serialPortConfiguration.sendEol$ = chr(13) serialPortConfiguration.receiveEol$ = chr(13) serialPortConfiguration.invertSignals = false serialPortConfiguration.gps = false Sign.serialPortConfigurations[0] = serialPortConfiguration else serialPortConfigurationsXML = BrightAuthor.meta.SerialPortConfiguration for each serialPortConfigurationXML in serialPortConfigurationsXML serialPortConfiguration = CreateObject("roAssociativeArray") serialPortSpeed% = int(val(serialPortConfigurationXML.baudRate.GetText())) serialPortConfiguration.serialPortSpeed% = serialPortSpeed% dataBits$ = serialPortConfigurationXML.dataBits.GetText() parity$ = serialPortConfigurationXML.parity.GetText() stopBits$ = serialPortConfigurationXML.stopBits.GetText() serialPortMode$ = dataBits$ + parity$ + stopBits$ serialPortConfiguration.serialPortMode$ = serialPortMode$ protocol$ = serialPortConfigurationXML.protocol.GetText() if protocol$ = "" then serialPortConfiguration.protocol$ = "ASCII" else serialPortConfiguration.protocol$ = serialPortConfigurationXML.protocol.GetText() endif serialPortConfiguration.sendEol$ = GetEolFromSpec(serialPortConfigurationXML.sendEol.GetText()) serialPortConfiguration.receiveEol$ = GetEolFromSpec(serialPortConfigurationXML.receiveEol.GetText()) serialPortConfiguration.invertSignals = false if lcase(serialPortConfigurationXML.invertSignals.GetText()) = "true" then serialPortConfiguration.invertSignals = true endif port$ = serialPortConfigurationXML.port.GetText() port% = int(val(port$)) connectedDevice$ = serialPortConfigurationXML.connectedDevice.GetText() if connectedDevice$ = "GPS" then serialPortConfiguration.gps = true bsp.gpsConfigured = true bsp.gpsPort$ = port$ else serialPortConfiguration.gps = false endif Sign.serialPortConfigurations[port%] = serialPortConfiguration next endif ' parse script plugins scriptPluginsContainer = BrightAuthor.meta.scriptPlugins if scriptPluginsContainer.Count() = 1 then scriptPluginsXML = scriptPluginsContainer.GetChildElements() for each scriptPluginXML in scriptPluginsXML scriptPlugin = newScriptPlugin(scriptPluginXML) bsp.scriptPlugins.push(scriptPlugin) next endif ' first pass parse of user variables ' bsp.autoCreateMediaCounterVariables = GetBoolFromString(BrightAuthor.meta.autoCreateMediaCounterVariables.GetText(), false) bsp.networkedVariablesUpdateInterval% = 300 networkedVariablesUpdateInterval$ = BrightAuthor.meta.networkedVariablesUpdateInterval.GetText() if networkedVariablesUpdateInterval$ <> "" then bsp.networkedVariablesUpdateInterval% = int(val(networkedVariablesUpdateInterval$)) endif bsp.variablesDBExists = false bsp.ReadVariablesDB(bsp.activePresentation$) variablePosition% = 0 userVariableSetXML = BrightAuthor.meta.userVariables if userVariableSetXML.Count() = 1 then userVariablesXML = userVariableSetXML.GetChildElements() if type(userVariablesXML) = "roXMLList" and userVariablesXML.Count() > 0 then bsp.privateDBSectionId% = bsp.GetDBSectionId(bsp.activePresentation$) if bsp.privateDBSectionId% < 0 then bsp.AddDBSection(bsp.activePresentation$) bsp.privateDBSectionId% = bsp.GetDBSectionId(bsp.activePresentation$) endif bsp.privateBrightAuthorCategoryId% = bsp.GetDBCategoryId(bsp.privateDBSectionId%, "BrightAuthor") if bsp.privateBrightAuthorCategoryId% < 0 then bsp.AddDBCategory(bsp.privateDBSectionId%, "BrightAuthor") bsp.privateBrightAuthorCategoryId% = bsp.GetDBCategoryId(bsp.privateDBSectionId%, "BrightAuthor") endif bsp.sharedDBSectionId% = bsp.GetDBSectionId("Shared") if bsp.sharedDBSectionId% < 0 then bsp.AddDBSection("Shared") bsp.sharedDBSectionId% = bsp.GetDBSectionId("Shared") endif bsp.sharedBrightAuthorCategoryId% = bsp.GetDBCategoryId(bsp.sharedDBSectionId%, "BrightAuthor") if bsp.sharedBrightAuthorCategoryId% < 0 then bsp.AddDBCategory(bsp.sharedDBSectionId%, "BrightAuthor") bsp.sharedBrightAuthorCategoryId% = bsp.GetDBCategoryId(bsp.sharedDBSectionId%, "BrightAuthor") endif userVariables = bsp.currentUserVariables for each userVariableXML in userVariablesXML access$ = userVariableXML.access.GetText() if access$ = "Shared" then categoryId% = bsp.sharedBrightAuthorCategoryId% else categoryId% = bsp.privateBrightAuthorCategoryId% endif name$ = userVariableXML.name.GetText() defaultValue$ = userVariableXML.defaultValue.GetText() if not userVariables.DoesExist(name$) then bsp.AddDBVariable(categoryId%, name$, defaultValue$, "", 0) userVariable = newUserVariable(bsp, name$, defaultValue$, defaultValue$, "", access$) userVariables.AddReplace(name$, userVariable) else userVariable = userVariables.Lookup(name$) if userVariable.defaultValue$ <> defaultValue$ then userVariable.defaultValue$ = defaultValue$ bsp.UpdateDBVariableDefaultValue(categoryId%, name$, defaultValue$) endif endif userVariable.position% = variablePosition% variablePosition% = variablePosition% + 1 ' if the user variable is specified with a system variable, set its value userVariable.systemVariable$ = userVariableXML.systemVariable.GetText() if userVariable.systemVariable$ = "serialNumber" then userVariable.SetCurrentValue(bsp.sysInfo.deviceUniqueID$, false) else if userVariable.systemVariable$ = "ipAddressWired" then userVariable.SetCurrentValue(bsp.sysInfo.ipAddressWired$, false) else if userVariable.systemVariable$ = "ipAddressWireless" then userVariable.SetCurrentValue(bsp.sysInfo.ipAddressWireless$, false) else if userVariable.systemVariable$ = "firmwareVersion" then userVariable.SetCurrentValue(bsp.sysInfo.deviceFWVersion$, false) else if userVariable.systemVariable$ = "scriptVersion" then userVariable.SetCurrentValue(bsp.sysInfo.autorunVersion$, false) else if userVariable.systemVariable$ = "rfChannelCount" then userVariable.SetCurrentValue(StripLeadingSpaces(stri(bsp.scannedChannels.Count())), false) else if userVariable.systemVariable$ = "rfChannelName" or userVariable.systemVariable$ = "rfVirtualChannel" then userVariable.SetCurrentValue("", false) else if userVariable.systemVariable$ = "tunerScanPercentageComplete" then userVariable.SetCurrentValue("0", false) else if userVariable.systemVariable$ = "edidMonitorSerialNumber" then userVariable.SetCurrentValue(bsp.sysInfo.edidMonitorSerialNumber$, false) else if userVariable.systemVariable$ = "edidYearOfManufacture" then userVariable.SetCurrentValue(bsp.sysInfo.edidYearOfManufacture$, false) else if userVariable.systemVariable$ = "edidMonitorName" then userVariable.SetCurrentValue(bsp.sysInfo.edidMonitorName$, false) else if userVariable.systemVariable$ = "edidManufacturer" then userVariable.SetCurrentValue(bsp.sysInfo.edidManufacturer$, false) else if userVariable.systemVariable$ = "edidUnspecifiedText" then userVariable.SetCurrentValue(bsp.sysInfo.edidUnspecifiedText$, false) else if userVariable.systemVariable$ = "edidSerialNumber" then userVariable.SetCurrentValue(bsp.sysInfo.edidSerialNumber$, false) else if userVariable.systemVariable$ = "edidManufacturerProductCode" then userVariable.SetCurrentValue(bsp.sysInfo.edidManufacturerProductCode$, false) else if userVariable.systemVariable$ = "edidWeekOfManufacture" then userVariable.SetCurrentValue(bsp.sysInfo.edidWeekOfManufacture$, false) else if userVariable.systemVariable$ = "activePresentation" then if IsString(bsp.activePresentation$) then userVariable.SetCurrentValue(bsp.activePresentation$, false) else userVariable.SetCurrentValue("", false) endif endif ' record networked information - parse in 2nd pass userVariable.url$ = "" userVariable.liveDataFeedName$ = "" url$ = userVariableXML.url.GetText() if url$ <> "" then userVariable.url$ = url$ else if userVariableXML.liveDataFeedName.GetText() <> "" then userVariable.liveDataFeedName$ = CleanName(userVariableXML.liveDataFeedName.GetText()) endif endif next endif endif ' parse live data feeds liveDataFeedsContainer = BrightAuthor.meta.liveDataFeeds if liveDataFeedsContainer.Count() = 1 then liveDataFeedsXML = liveDataFeedsContainer.GetChildElements() for each liveDataFeedXML in liveDataFeedsXML liveDataFeed = newLiveDataFeed(bsp, liveDataFeedXML) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) next endif ' second pass parse of user variables if type(userVariables) = "roAssociativeArray" then for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if type(userVariable.url$) <> "Invalid" and userVariable.url$ <> "" then ' old format liveDataFeed = bsp.liveDataFeeds.Lookup(userVariable.url$) if type(liveDataFeed) <> "roAssociativeArray" then url = newTextParameterValue(userVariable.url$) liveDataFeed = newLiveDataFeedFromOldDataFormat(bsp, url, bsp.networkedVariablesUpdateInterval%) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif userVariable.liveDataFeed = liveDataFeed else if type(userVariable.liveDataFeedName$) <> "Invalid" and userVariable.liveDataFeedName$ <> "" then ' new format userVariable.liveDataFeed = bsp.liveDataFeeds.Lookup(userVariable.liveDataFeedName$) endif next endif ' reset variables if indicated in sign if BrightAuthor.meta.resetVariablesOnPresentationStart.GetText() <> "" and lcase(BrightAuthor.meta.resetVariablesOnPresentationStart.GetText()) = "true" then bsp.ResetVariables() endif ' parse HTML sites htmlSitesContainer = BrightAuthor.meta.htmlSites if htmlSitesContainer.Count() = 1 then htmlSitesXML = htmlSitesContainer.GetChildElements() for each htmlSiteXML in htmlSitesXML htmlSite = newHTMLSite(bsp, htmlSiteXML) bsp.htmlSites.AddReplace(htmlSite.name$, htmlSite) next endif ' parse presentations presentationsContainer = BrightAuthor.meta.presentationIdentifiers if presentationsContainer.Count() = 1 then presentationsXML = presentationsContainer.GetChildElements() for each presentationXML in presentationsXML presentation = newPresentation(bsp, presentationXML) bsp.presentations.AddReplace(presentation.name$, presentation) next endif ' get list of additional files to publish additionalPublishedFilesXML = BrightAuthor.meta.additionalFileToPublish for each additionalPublishedFileXML in additionalPublishedFilesXML additionalPublishedFile = {} additionalPublishedFile.fileName$ = additionalPublishedFileXML.GetText() additionalPublishedFile.filePath$ = GetPoolFilePath(bsp.assetPoolFiles, additionalPublishedFile.fileName$) bsp.additionalPublishedFiles.push(additionalPublishedFile) next ' parse BoseProducts.xml boseProductsXML = BrightAuthor.meta.boseProduct if boseProductsXML.Count() > 0 then Sign.boseProducts = CreateObject("roAssociativeArray") for each boseProductXML in boseProductsXML boseProduct = CreateObject("roAssociativeArray") boseProduct.productName$ = boseProductXML.productName.GetText() boseProduct.port$ = boseProductXML.port.GetText() Sign.boseProducts.AddReplace(boseProduct.productName$, boseProduct) boseProductSpec = bsp.GetBoseProductSpec(boseProduct.productName$) if type(boseProductSpec) = "roAssociativeArray" then port% = int(val(boseProduct.port$)) serialPortConfiguration = Sign.serialPortConfigurations[port%] serialPortConfiguration.serialPortSpeed% = boseProductSpec.baudRate% serialPortConfiguration.serialPortMode$ = boseProductSpec.dataBits$ + boseProductSpec.parity$ + boseProductSpec.stopBits$ serialPortConfiguration.sendEol$ = GetEolFromSpec(boseProductSpec.sendEol$) serialPortConfiguration.receiveEol$ = GetEolFromSpec(boseProductSpec.receiveEol$) serialPortConfiguration.invertSignals = boseProductSpec.invertSignals endif next endif Sign.tripleUSBPort$ = BrightAuthor.meta.tripleUSBPort.GetText() if BrightAuthor.meta.tripleUSBPort.Count() = 1 then port% = int(val(BrightAuthor.meta.tripleUSBPort.GetText())) serialPortConfiguration = Sign.serialPortConfigurations[port%] serialPortConfiguration.serialPortSpeed% = 9600 serialPortConfiguration.serialPortMode$ = "8N1" endif ' set default serial port speed, mode bsp.serialPortConfigurations = CreateObject("roArray", 6, true) for i% = 0 to 5 if type(Sign.serialPortConfigurations[i%]) = "roAssociativeArray" then serialPortConfiguration = CreateObject("roAssociativeArray") serialPortConfiguration.serialPortSpeed% = Sign.serialPortConfigurations[i%].serialPortSpeed% serialPortConfiguration.serialPortMode$ = Sign.serialPortConfigurations[i%].serialPortMode$ serialPortConfiguration.protocol$ = Sign.serialPortConfigurations[i%].protocol$ serialPortConfiguration.sendEol$ = Sign.serialPortConfigurations[i%].sendEol$ serialPortConfiguration.receiveEol$ = Sign.serialPortConfigurations[i%].receiveEol$ serialPortConfiguration.invertSignals = Sign.serialPortConfigurations[i%].invertSignals bsp.serialPortConfigurations[i%] = serialPortConfiguration endif next Sign.udpReceivePort = int(val(BrightAuthor.meta.udpReceiverPort.GetText())) Sign.udpSendPort = int(val(BrightAuthor.meta.udpDestinationPort.GetText())) Sign.udpAddressType$ = BrightAuthor.meta.udpDestinationAddressType.GetText() if Sign.udpAddressType$ = "" then Sign.udpAddressType$ = "IPAddress" Sign.udpAddress$ = BrightAuthor.meta.udpDestinationAddress.GetText() Sign.enableEnhancedSynchronization = false Sign.deviceIsSyncMaster = false Sign.ptpDomain$ = 0 enableEnhancedSynchronization = BrightAuthor.meta.enableEnhancedSynchronization if type(enableEnhancedSynchronization) = "roXMLList" and enableEnhancedSynchronization.Count() = 1 then Sign.enableEnhancedSynchronization = true enableEnhancedSynchronizationAttrs = enableEnhancedSynchronization.GetAttributes() deviceIsSyncMasterStr = enableEnhancedSynchronizationAttrs["deviceIsSyncMaster"] if lcase(deviceIsSyncMasterStr) = "true" then Sign.deviceIsSyncMaster = true targetSyncMasterInRegistry = "1" else targetSyncMasterInRegistry = "0" endif Sign.ptpDomain$ = enableEnhancedSynchronizationAttrs["ptpDomain"] rebootRequired = false ' check the sync master value in the registry. if it does not exist or is different, set it and reboot. ' check the domain value in the registry. if it does not exist or is different, set it and reboot. ptpDomainInRegistry$ = bsp.registrySection.Read("ptp_domain") if ptpDomainInRegistry$ <> Sign.ptpDomain$ then bsp.registrySection.Write("ptp_domain", Sign.ptpDomain$) rebootRequired = true endif ' check the syncMaster value in the registry. if it does not exist or is different, set it and reboot. syncMasterInRegistry$ = bsp.registrySection.Read("sync_master") if syncMasterInRegistry$ <> targetSyncMasterInRegistry then bsp.registrySection.Write("sync_master", targetSyncMasterInRegistry) rebootRequired = true endif if rebootRequired then bsp.registrySection.Flush() RebootSystem() endif endif Sign.flipCoordinates = false flipCoordinates$ = BrightAuthor.meta.flipCoordinates.GetText() if flipCoordinates$ = "true" then Sign.flipCoordinates = true Sign.touchCursorDisplayMode$ = BrightAuthor.meta.touchCursorDisplayMode.GetText() audioInSampleRate$ = BrightAuthor.meta.audioInSampleRate.GetText() if audioInSampleRate$ <> "" then Sign.audioInSampleRate% = int(val(audioInSampleRate$)) else Sign.audioInSampleRate% = 48000 endif Sign.gpio0Config = BrightAuthor.meta.gpio0.GetText() if Sign.gpio0Config = "input" then controlPort.EnableInput(0) else controlPort.EnableOutput(0) endif Sign.gpio1Config = BrightAuthor.meta.gpio1.GetText() if Sign.gpio1Config = "input" then controlPort.EnableInput(1) else controlPort.EnableOutput(1) endif Sign.gpio2Config = BrightAuthor.meta.gpio2.GetText() if Sign.gpio2Config = "input" then controlPort.EnableInput(2) else controlPort.EnableOutput(2) endif Sign.gpio3Config = BrightAuthor.meta.gpio3.GetText() if Sign.gpio3Config = "input" then controlPort.EnableInput(3) else controlPort.EnableOutput(3) endif Sign.gpio4Config = BrightAuthor.meta.gpio4.GetText() if Sign.gpio4Config = "input" then controlPort.EnableInput(4) else controlPort.EnableOutput(4) endif Sign.gpio5Config = BrightAuthor.meta.gpio5.GetText() if Sign.gpio5Config = "input" then controlPort.EnableInput(5) else controlPort.EnableOutput(5) endif Sign.gpio6Config = BrightAuthor.meta.gpio6.GetText() if Sign.gpio6Config = "input" then controlPort.EnableInput(6) else controlPort.EnableOutput(6) endif Sign.gpio7Config = BrightAuthor.meta.gpio7.GetText() if Sign.gpio7Config = "input" then controlPort.EnableInput(7) else controlPort.EnableOutput(7) endif audio1MinVolume$ = BrightAuthor.meta.audio1MinVolume.GetText() if audio1MinVolume$ <> "" then Sign.audio1MinVolume% = int(val(audio1MinVolume$)) else Sign.audio1MinVolume% = 0 endif audio1MaxVolume$ = BrightAuthor.meta.audio1MaxVolume.GetText() if audio1MaxVolume$ <> "" then Sign.audio1MaxVolume% = int(val(audio1MaxVolume$)) else Sign.audio1MaxVolume% = 100 endif audio2MinVolume$ = BrightAuthor.meta.audio2MinVolume.GetText() if audio2MinVolume$ <> "" then Sign.audio2MinVolume% = int(val(audio2MinVolume$)) else Sign.audio2MinVolume% = 0 endif audio2MaxVolume$ = BrightAuthor.meta.audio2MaxVolume.GetText() if audio2MaxVolume$ <> "" then Sign.audio2MaxVolume% = int(val(audio2MaxVolume$)) else Sign.audio2MaxVolume% = 100 endif audio3MinVolume$ = BrightAuthor.meta.audio3MinVolume.GetText() if audio3MinVolume$ <> "" then Sign.audio3MinVolume% = int(val(audio3MinVolume$)) else Sign.audio3MinVolume% = 0 endif audio3MaxVolume$ = BrightAuthor.meta.audio3MaxVolume.GetText() if audio3MaxVolume$ <> "" then Sign.audio3MaxVolume% = int(val(audio3MaxVolume$)) else Sign.audio3MaxVolume% = 100 endif usbMinVolume$ = BrightAuthor.meta.usbMinVolume.GetText() if usbMinVolume$ <> "" then Sign.usbMinVolume% = int(val(usbMinVolume$)) else Sign.usbMinVolume% = 0 endif usbMaxVolume$ = BrightAuthor.meta.usbMaxVolume.GetText() if usbMaxVolume$ <> "" then Sign.usbMaxVolume% = int(val(usbMaxVolume$)) else Sign.usbMaxVolume% = 100 endif hdmiMinVolume$ = BrightAuthor.meta.hdmiMinVolume.GetText() if hdmiMinVolume$ <> "" then Sign.hdmiMinVolume% = int(val(hdmiMinVolume$)) else Sign.hdmiMinVolume% = 0 endif hdmiMaxVolume$ = BrightAuthor.meta.hdmiMaxVolume.GetText() if hdmiMaxVolume$ <> "" then Sign.hdmiMaxVolume% = int(val(hdmiMaxVolume$)) else Sign.hdmiMaxVolume% = 100 endif spdifMinVolume$ = BrightAuthor.meta.spdifMinVolume.GetText() if spdifMinVolume$ <> "" then Sign.spdifMinVolume% = int(val(spdifMinVolume$)) else Sign.spdifMinVolume% = 0 endif spdifMaxVolume$ = BrightAuthor.meta.spdifMaxVolume.GetText() if spdifMaxVolume$ <> "" then Sign.spdifMaxVolume% = int(val(spdifMaxVolume$)) else Sign.spdifMaxVolume% = 100 endif inactivityTimeout$ = lcase(BrightAuthor.meta.inactivityTimeout.GetText()) if inactivityTimeout$ = "true" then bsp.inactivityTimeout = true else bsp.inactivityTimeout = false endif inactivityTime$ = BrightAuthor.meta.inactivityTime.GetText() if len(inactivityTime$) > 0 then bsp.inactivityTime% = int(val(inactivityTime$)) else bsp.inactivityTime% = 0 endif ' RF Channel Data rfChannelDataFile = BrightAuthor.meta.rfChannelDataFile.GetText() if IsString(rfChannelDataFile) and rfChannelDataFile <> "" then rfChannelDataFilePath$ = GetPoolFilePath(bsp.assetPoolFiles, rfChannelDataFile) if rfChannelDataFilePath$ <> "" then tunerData$ = ReadAsciiFile(rfChannelDataFilePath$) if tunerData$ <> "" then cdataStartTag$ = "" docEndTag$ = "" cdataIndex% = instr(1, tunerData$, cdataStartTag$) if cdataIndex% > 0 then fwXML$ = mid(tunerData$, cdataIndex% + len(cdataStartTag$) + 1, len(tunerData$) - (cdataIndex% + len(cdataStartTag$) + 1) - (len(cdataEndTag$) + len(docEndTag$) + 1)) channelManager = CreateObject("roChannelManager") if type(channelManager) = "roChannelManager" then channelManager.ClearChannelData() ok = channelManager.ImportFromXml(fwXML$) if ok then bsp.scannedChannels = GetScannedChannels() endif endif endif endif endif endif ' get zones zoneList = BrightAuthor.zones.zone if type(zoneList) <> "roXMLList" then print "Invalid XML file - zone list not found" : stop numZones% = zoneList.Count() Sign.zonesHSM = CreateObject("roArray", numZones%, true) Sign.videoZoneHSM = invalid for each zone in zoneList bsZoneHSM = newZoneHSM(bsp, msgPort, Sign, zone, globalVariables) Sign.zonesHSM.push(bsZoneHSM) if (bsZoneHSM.type$ = "VideoOrImages" or bsZoneHSM.type$ = "VideoOnly") or Sign.videoZoneHSM = invalid then Sign.videoZoneHSM = bsZoneHSM endif next ' audioOutput, audioMode and audioMapping were set incorrectly on earlier versions of presentations ' if version% < 4 then ' for each bsZone in Sign.zones ' if bsZone.type$ = "VideoOrImages" or bsZone.type$ = "VideoOnly" then ' bsZone.audioOutput% = 4 ' bsZone.audioMode% = 0 ' bsZone.audioMapping% = 0 ' endif ' next ' endif return Sign End Function 'endregion 'region User Variable DB Sub ResetUserVariable(postMsg As Boolean) m.currentValue$ = m.defaultValue$ m.bsp.UpdateDBVariable(m.bsp.GetCategoryIdFromAccess(m.access$), m.name$, m.currentValue$) if postMsg then userVariableChanged = CreateObject("roAssociativeArray") userVariableChanged["EventType"] = "USER_VARIABLE_CHANGE" userVariableChanged["UserVariable"] = m m.bsp.msgPort.PostMessage(userVariableChanged) endif m.bsp.SendUDPNotification("refresh") End Sub Sub SetCurrentUserVariableValue(value$ As String, postMsg As Boolean) m.currentValue$ = value$ m.bsp.UpdateDBVariable(m.bsp.GetCategoryIdFromAccess(m.access$), m.name$, m.currentValue$) if postMsg then userVariableChanged = CreateObject("roAssociativeArray") userVariableChanged["EventType"] = "USER_VARIABLE_CHANGE" userVariableChanged["UserVariable"] = m m.bsp.msgPort.PostMessage(userVariableChanged) m.bsp.SendUDPNotification("refresh") endif End Sub Function GetCurrentUserVariableValue() As Object return m.currentValue$ End Function Sub IncrementUserVariable() currentValue% = int(val(m.currentValue$)) currentValue% = currentValue% + 1 m.currentValue$ = StripLeadingSpaces(stri(currentValue%)) m.bsp.UpdateDBVariable(m.bsp.GetCategoryIdFromAccess(m.access$), m.name$, m.currentValue$) m.bsp.SendUDPNotification("refresh") End Sub Function newUserVariable(bsp As Object, name$ As String, currentValue$ As String, defaultValue$ As String, mediaUrl$ As String, access$ As String) As Object userVariable = CreateObject("roAssociativeArray") userVariable.GetCurrentValue = GetCurrentUserVariableValue userVariable.SetCurrentValue = SetCurrentUserVariableValue userVariable.Increment = IncrementUserVariable userVariable.Reset = ResetUserVariable userVariable.bsp = bsp userVariable.name$ = name$ userVariable.currentValue$ = currentValue$ userVariable.defaultValue$ = defaultValue$ userVariable.mediaUrl$ = mediaUrl$ userVariable.access$ = access$ userVariable.liveDataFeed = invalid return userVariable End Function Function GetCategoryIdFromAccess(access$ As String) As Integer if lcase(access$) = "shared" then categoryId% = m.sharedBrightAuthorCategoryId% else categoryId% = m.privateBrightAuthorCategoryId% endif return categoryId% End Function Sub UpdateDBVariable(categoryId% As Integer, name$ As String, currentValue$ As String) params = { cv_param: currentValue$, vn_param: name$, cri_param: categoryId% } m.userVariablesDB.RunBackground("UPDATE Variables2 SET CurrentValue=:cv_param WHERE VariableName=:vn_param AND CategoryReferenceId=:cri_param;", params) End Sub Sub UpdateDBVariableDefaultValue(categoryId% As Integer, name$ As String, defaultValue$ As String) params = { dv_param: defaultValue$, vn_param: name$, cri_param: categoryId% } m.userVariablesDB.RunBackground("UPDATE Variables2 SET DefaultValue=:dv_param WHERE VariableName=:vn_param AND CategoryReferenceId=:cri_param;", params) End Sub Sub UpdateDBVariableMediaUrl(categoryId% As Integer, name$ As String, mediaUrl$ As String) params = { mu_param: mediaUrl$, vn_param: name$, cri_param: categoryId% } m.userVariablesDB.RunBackground("UPDATE Variables2 SET MediaUrl=:mu_param WHERE VariableName=:vn_param AND CategoryReferenceId=:cri_param;", params) End Sub Sub UpdateDBVariablePosition(categoryId% As Integer, name$ As String, position% As Integer) params = { p_param: position%, vn_param: name$, cri_param: categoryId% } m.userVariablesDB.RunBackground("UPDATE Variables2 SET Position=:p_param WHERE VariableName=:vn_param AND CategoryReferenceId=:cri_param;", params) End Sub Sub AddDBVariable(categoryId% As Integer, name$ As String, defaultValue$ As String, mediaUrl$ As String, position% As Integer) insertSQL$ = "INSERT INTO Variables2 (CategoryReferenceId, VariableName, CurrentValue, DefaultValue, MediaUrl, Position) VALUES(?,?,?,?,?,?);" params = CreateObject("roArray", 6, false) params[ 0 ] = categoryId% params[ 1 ] = name$ params[ 2 ] = defaultValue$ params[ 3 ] = defaultValue$ params[ 4 ] = mediaUrl$ params[ 5 ] = position% m.ExecuteDBInsert(insertSQL$, params) End Sub Sub AddDBSection(sectionName$ As String) insertSQL$ = "INSERT INTO Sections (SectionName) VALUES(:name_param);" params = { name_param: sectionName$ } m.ExecuteDBInsert(insertSQL$, params) End Sub Sub DeleteDBVariable(categoryId% As Integer, variableName$ As String) SQLITE_COMPLETE = 100 params = { :uv_param: variableName$ } delete$ = "DELETE FROM Variables2 WHERE VariableName =:uv_param AND CategoryReferenceId = " + StripLeadingSpaces(stri(categoryId%)) + ";" deleteStatement = m.userVariablesDB.CreateStatement(delete$) if type(deleteStatement) <> "roSqliteStatement" then m.diagnostics.PrintDebug("DeleteStatement failure - " + delete$) stop endif bindResult = deleteStatement.BindByName(params) if not bindResult then m.diagnostics.PrintDebug("Bind failure") stop endif sqlResult = deleteStatement.Run() if sqlResult <> SQLITE_COMPLETE m.diagnostics.PrintDebug("sqlResult <> SQLITE_COMPLETE") endif deleteStatement.Finalise() End Sub Sub AddDBCategory(sectionId% As Integer, categoryName$ As String) insertSQL$ = "INSERT INTO Categories (SectionReferenceId, CategoryName) VALUES(?,?);" params = CreateObject("roArray", 2, false) params[ 0 ] = sectionId% params[ 1 ] = categoryName$ m.ExecuteDBInsert(insertSQL$, params) End Sub Sub ExecuteDBInsert(insert$ As String, params As Object) SQLITE_COMPLETE = 100 insertStatement = m.userVariablesDB.CreateStatement(insert$) if type(insertStatement) <> "roSqliteStatement" then m.diagnostics.PrintDebug("CreateStatement failure - " + insert$) stop endif if type(params) = "roArray" then bindResult = insertStatement.BindByOffset(params) else bindResult = insertStatement.BindByName(params) endif if not bindResult then m.diagnostics.PrintDebug("Bind failure") stop endif sqlResult = insertStatement.Run() if sqlResult <> SQLITE_COMPLETE m.diagnostics.PrintDebug("sqlResult <> SQLITE_COMPLETE") endif insertStatement.Finalise() End Sub Sub ExecuteDBSelect(select$ As String, resultsCallback As Object, selectData As Object, params As Object) SQLITE_ROWS = 102 selectStmt = m.userVariablesDB.CreateStatement(select$) if type(selectStmt) <> "roSqliteStatement" then m.diagnostics.PrintDebug("CreateStatement failure - " + select$) stop endif bindResult = true if type(params) = "roArray" then bindResult = selectStmt.BindByOffset(params) else if type(params) = "roAssociativeArray" then bindResult = selectStmt.BindByName(params) endif if not bindResult then m.diagnostics.PrintDebug("Bind failure") stop endif sqlResult = selectStmt.Run() while sqlResult = SQLITE_ROWS resultsData = selectStmt.GetData() resultsCallback(resultsData, selectData) sqlResult = selectStmt.Run() end while selectStmt.Finalise() End Sub Sub GetDBVersionCallback(resultsData As Object, selectData As Object) selectData.version$ = resultsData["Version"] End Sub Function GetDBVersion() As String selectData = {} selectData.version$ = "" select$ = "SELECT SchemaVersion.Version FROM SchemaVersion;" m.ExecuteDBSelect(select$, GetDBVersionCallback, selectData, invalid) return selectData.version$ End Function Sub GetDBCategoryIdCallback(resultsData As Object, selectData As Object) selectData.categoryId% = resultsData["CategoryId"] End Sub Function GetDBCategoryId(sectionId% As Integer, categoryName$ As String) As Object selectData = {} selectData.categoryId% = -1 select$ = "SELECT CategoryId FROM Categories WHERE SectionReferenceId = " + StripLeadingSpaces(stri(sectionId%)) + " AND CategoryName='" + categoryName$ + "';" m.ExecuteDBSelect(select$, GetDBCategoryIdCallback, selectData, invalid) return selectData.categoryId% End Function Sub GetDBSectionNamesCallback(resultsData As Object, selectData As Object) selectData.sections.push(resultsData["SectionName"]) End Sub Function GetDBSectionNames() As Object selectData = {} selectData.sections = [] select$ = "SELECT Sections.SectionName FROM Sections;" m.ExecuteDBSelect(select$, GetDBSectionNamesCallback, selectData, invalid) return selectData.sections End Function Sub GetDBSectionIdCallback(resultsData As Object, selectData As Object) selectData.sectionId% = resultsData["SectionId"] End Sub Function GetDBSectionId(sectionName$ As String) As Object selectData = {} selectData.sectionId% = -1 params = { sn_param: sectionName$ } select$ = "SELECT SectionId FROM Sections WHERE SectionName =:sn_param;" m.ExecuteDBSelect(select$, GetDBSectionIdCallback, selectData, params) return selectData.sectionId% End Function Sub ReadSchema1TablesCallback(resultsData As Object, selectData As Object) sectionName$ = resultsData["SectionName"] if selectData.userVariableSets.DoesExist(sectionName$) then userVariables = selectData.userVariableSets.Lookup(sectionName$) else userVariables = [] selectData.userVariableSets.AddReplace(sectionName$, userVariables) endif variableName$ = resultsData["VariableName"] currentValue$ = resultsData["CurrentValue"] defaultValue$ = resultsData["DefaultValue"] userVariable = newUserVariable(m, variableName$, currentValue$, defaultValue$, "", "") userVariables.push(userVariable) End Sub Function ReadSchema1Tables() As Object selectData = {} selectData.userVariableSets = {} select$ = "SELECT Sections.SectionName, Variables.VariableName, Variables.CurrentValue, Variables.DefaultValue FROM Variables INNER JOIN Sections ON Sections.SectionId = Variables.SectionReferenceId ORDER BY Sections.SectionName, Variables.VariableId;" m.ExecuteDBSelect(select$, ReadSchema1TablesCallback, selectData, invalid) return selectData.userVariableSets End Function Sub GetUserVariablesGivenCategoryCallback(resultsData As Object, selectData As Object) variableName$ = resultsData["VariableName"] currentValue$ = resultsData["CurrentValue"] defaultValue$ = resultsData["DefaultValue"] mediaUrl$ = resultsData["MediaUrl"] ' access level isn't needed for this list userVariable = newUserVariable(m, variableName$, currentValue$, defaultValue$, mediaUrl$, "") selectData.userVariablesList.push(userVariable) End Sub Function GetUserVariablesGivenCategory(sectionName$ As String, includeSharedSection As Boolean, categoryName$ As String, sortByPosition As Boolean) As Object selectData = {} selectData.userVariablesList = [] select$ = "SELECT Variables2.* FROM Variables2 INNER JOIN Sections ON Sections.SectionId=Categories.SectionReferenceId INNER JOIN Categories ON Categories.CategoryId=Variables2.CategoryReferenceId WHERE Categories.CategoryName='" + categoryName$ + "' AND (" if includeSharedSection then select$ = select$ + "Sections.SectionName='Shared' OR " endif select$ = select$ + "Sections.SectionName='" + sectionName$ + "')" if sortByPosition then select$ = select$ + " ORDER BY Variables2.Position" endif select$ = select$ + ";" m.ExecuteDBSelect(select$, GetUserVariablesGivenCategoryCallback, selectData, invalid) return selectData.userVariablesList End Function Sub DoGetCategoriesCallback(resultsData As Object, selectData As Object) categoryName$ = resultsData["CategoryName"] if (categoryName$ <> "BrightAuthor") or (selectData.includeBrightAuthorCategory) then selectData.userVariableCategories.push(categoryName$) endif End Sub Function DoGetCategories(sectionName$ As String, includeShared As Boolean, includeBrightAuthorCategory) As Object selectData = {} selectData.userVariableCategories = [] selectData.includeBrightAuthorCategory = includeBrightAuthorCategory params = { :sn_param: sectionName$ } select$ = "SELECT CategoryName FROM Categories INNER JOIN Sections ON Sections.SectionId = Categories.SectionReferenceId WHERE " if includeShared then select$ = select$ + "Sections.SectionName = 'Shared' OR " endif select$ = select$ + "Sections.SectionName =:sn_param;" selectStmt = m.userVariablesDB.CreateStatement(select$) if type(selectStmt) <> "roSqliteStatement" then m.diagnostics.PrintDebug("CreateStatement failure - " + select$) stop endif bindResult = selectStmt.BindByName(params) if not bindResult then m.diagnostics.PrintDebug("Bind failure") stop endif m.ExecuteDBSelect(select$, DoGetCategoriesCallback, selectData, params) return selectData.userVariableCategories End Function Sub ReadVariablesCallback(resultsData As Object, selectData As Object) sectionName$ = resultsData["SectionName"] if sectionName$ = selectData.presentationName$ or sectionName$ = "Shared" then variableName$ = resultsData["VariableName"] currentValue$ = resultsData["CurrentValue"] defaultValue$ = resultsData["DefaultValue"] mediaUrl$ = resultsData["MediaUrl"] if sectionName$ = "Shared" then access$ = "Shared" else access$ = "Private" endif userVariable = newUserVariable(selectData.bsp, variableName$, currentValue$, defaultValue$, mediaUrl$, access$) userVariable.position% = -1 selectData.currentUserVariables.AddReplace(variableName$, userVariable) endif End Sub Function ReadVariables(presentationName$ As String) As Object selectData = {} selectData.bsp = m selectData.presentationName$ = presentationName$ selectData.currentUserVariables = {} select$ = "SELECT Sections.SectionName, Variables2.VariableName, Variables2.CurrentValue, Variables2.DefaultValue, Variables2.MediaUrl FROM Variables2 INNER JOIN Sections ON Sections.SectionId=Categories.SectionReferenceId INNER JOIN Categories ON Categories.CategoryId=Variables2.CategoryReferenceId WHERE Categories.CategoryName='BrightAuthor' ORDER BY Sections.SectionName;" m.ExecuteDBSelect(select$, ReadVariablesCallback, selectData, invalid) return selectData.currentUserVariables End Function Sub SetDBVersion(version$ As String) insertSQL$ = "INSERT INTO SchemaVersion (Version) VALUES(:version_param);" params = { version_param: version$ } m.ExecuteDBInsert(insertSQL$, params) End Sub Sub UpdateDBVersion(version$ As String) params = { v_param: version$ } m.userVariablesDB.RunBackground("UPDATE SchemaVersion SET Version=:v_param;", params) End Sub Sub CreateDBTable(statement$ As String) SQLITE_COMPLETE = 100 createStmt = m.userVariablesDB.CreateStatement(statement$) if type(createStmt) <> "roSqliteStatement" then m.diagnostics.PrintDebug("CreateStatement failure - " + statement$) stop endif sqlResult = createStmt.Run() if sqlResult <> SQLITE_COMPLETE m.diagnostics.PrintDebug("sqlResult <> SQLITE_COMPLETE") endif createStmt.Finalise() End Sub Sub DeleteDBTable(tableName$ As String) SQLITE_COMPLETE = 100 deleteStmt = m.userVariablesDB.CreateStatement("DROP TABLE " + tableName$) if type(deleteStmt) <> "roSqliteStatement" then m.diagnostics.PrintDebug("CreateStatement failure - DeleteDBTable") stop endif sqlResult = deleteStmt.Run() if sqlResult <> SQLITE_COMPLETE m.diagnostics.PrintDebug("sqlResult <> SQLITE_COMPLETE") endif deleteStmt.Finalise() End Sub Sub CreateSchema2Tables() m.CreateDBTable("CREATE TABLE Categories (CategoryId INTEGER PRIMARY KEY AUTOINCREMENT, SectionReferenceId INT, CategoryName TEXT);") m.CreateDBTable("CREATE TABLE Variables2 (VariableId INTEGER PRIMARY KEY AUTOINCREMENT, CategoryReferenceId INT, VariableName text, CurrentValue TEXT, DefaultValue TEXT, MediaUrl TEXT, Position INT);") End Sub Sub DropSchema1Tables() m.DeleteDBTable("Variables") End Sub Sub ReadVariablesDB(presentationName$ As String) SQLITE_ROWS = 102 m.variablesDBExists = true m.dbSchemaVersion$ = "2.0" if type(m.userVariablesDB) <> "roSqliteDatabase" then m.userVariablesDB = CreateObject("roSqliteDatabase") m.userVariablesDB.SetPort(m.msgPort) m.diagnostics.PrintDebug("Open userVariables.db") ok = m.userVariablesDB.Open("userVariables.db") if ok then version$ = m.GetDBVersion() if version$ <> m.dbSchemaVersion$ then userVariableSets = m.ReadSchema1Tables() m.CreateSchema2Tables() ' store old data in new tables for each sectionName in userVariableSets sectionId% = m.GetDBSectionId(sectionName) m.AddDBCategory(sectionId%, "BrightAuthor") categoryId% = m.GetDBCategoryId(sectionId%, "BrightAuthor") userVariables = userVariableSets.Lookup(sectionName) position% = 0 for each userVariable in userVariables m.AddDBVariable(categoryId%, userVariable.name$, userVariable.defaultValue$, "", position%) if userVariable.currentValue$ <> userVariable.defaultValue$ then m.UpdateDBVariable(categoryId%, userVariable.name$, userVariable.currentValue$) endif position% = position% + 1 next next ' drop old tables m.DropSchema1Tables() ' update version m.UpdateDBVersion(m.dbSchemaVersion$) endif else ok = m.userVariablesDB.Create("userVariables.db") if not ok then m.diagnostics.PrintDebug("Unable to create userVariables.db") return endif m.CreateDBTable("CREATE TABLE SchemaVersion (Version TEXT);") m.SetDBVersion(m.dbSchemaVersion$) m.CreateDBTable("CREATE TABLE Sections (SectionId INTEGER PRIMARY KEY AUTOINCREMENT, SectionName TEXT);") m.CreateSchema2Tables() endif endif ' get sections, variables for BrightAuthor category m.currentUserVariables = m.ReadVariables(presentationName$) End Sub Function GetUserVariablesByCategoryList(categoryName$ As String) As Object return m.GetUserVariablesGivenCategory(m.activePresentation$, true, categoryName$, true) End Function Function GetDBCategoryNames(sectionName$ As String) As Object return m.DoGetCategories(sectionName$, false, true) End Function Function GetUserVariableCategoryList(sectionName$ As String) As Object return m.DoGetCategories(sectionName$, true, false) End Function Function GetOrderedVariables(sectionName$ As String) As Object return m.GetUserVariablesGivenCategory(m.activePresentation$, true, "BrightAuthor", false) End Function Sub ExportVariablesDBToAsciiFile(file As Object) file.SendLine("Version" + chr(9) + m.dbSchemaVersion$) sectionNames = m.GetDBSectionNames() for each sectionName in sectionNames file.SendLine("Section" + chr(9) + sectionName) sectionId% = m.GetDBSectionId(sectionName) if sectionId% > 0 then categoryNames = m.GetDBCategoryNames(sectionName) for each categoryName in categoryNames file.SendLine("Category" + chr(9) + categoryName) userVariablesList = m.GetUserVariablesGivenCategory(sectionName, false, categoryName, false) for each userVariable in userVariablesList file.SendLine(userVariable.name$ + chr(9) + userVariable.currentValue$ + chr(9) + userVariable.defaultValue$) next next endif next End Sub Function GetUserVariable(variableName$ As String) As Object userVariable = invalid if m.currentUserVariables.DoesExist(variableName$) then userVariable = m.currentUserVariables.Lookup(variableName$) endif return userVariable End Function Sub DeleteVariable(variableName$ As String) userVariables = m.currentUserVariables if userVariables.DoesExist(variableName$) then userVariable = userVariables.Lookup(variableName$) if lcase(userVariable.access$) = "shared" then categoryId% = m.sharedBrightAuthorCategoryId% else categoryId% = m.privateBrightAuthorCategoryId% endif m.DeleteDBVariable(categoryId%, variableName$) userVariables.Delete(variableName$) endif m.SendUDPNotification("refresh") End Sub Sub ResetVariables() userVariables = m.currentUserVariables userVariableList = CreateObject("roList") for each variableName in userVariables userVariable = userVariables.Lookup(variableName) userVariableList.AddTail(userVariable) next for each userVariable in userVariableList userVariable.Reset(false) next userVariablesReset = CreateObject("roAssociativeArray") userVariablesReset["EventType"] = "USER_VARIABLES_RESET" m.msgPort.PostMessage(userVariablesReset) m.SendUDPNotification("refresh") End Sub 'endregion 'region newSign helpers Function GetBPConfiguration(bpHardware$ As String, bp900ConfigureAutomatically As Boolean, bp900Configuration% As Integer, bp200ConfigureAutomatically As Boolean, bp200Configuration% As Integer) As Integer if bpHardware$ = "BP900" then if bp900ConfigureAutomatically then return 0 else return bp900Configuration% endif else if bp200ConfigureAutomatically then return 0 else return bp200Configuration% endif endif End Function Function GetBoolFromString(str$ As String, defaultValue As Boolean) As Boolean if str$ = "" then return defaultValue else if lcase(str$) = "true" then return true else return false endif End Function Function GetIntFromString(str$ As String) As Integer if str$ <> "" then return int(val(str$)) else return 0 endif End Function 'endregion 'region ZoneHSM Function newZoneHSM(bsp As Object, msgPort As Object, sign As Object, zoneXML As Object, globalVariables As Object) As Object zoneType$ = zoneXML.type.GetText() ' create objects and read zone specific parameters if zoneType$ = "VideoOrImages" then zoneHSM = newVideoOrImagesZoneHSM(bsp, zoneXML) else if zoneType$ = "VideoOnly" then zoneHSM = newVideoZoneHSM(bsp, zoneXML) else if zoneType$ = "Images" then zoneHSM = newImagesZoneHSM(bsp, zoneXML) else if zoneType$ = "AudioOnly" then zoneHSM = newAudioZoneHSM(bsp, zoneXML) else if zoneType$ = "EnhancedAudio" then zoneHSM = newEnhancedAudioZoneHSM(bsp, zoneXML) else if zoneType$ = "Ticker" then zoneHSM = newTickerZoneHSM(bsp, sign, zoneXML) else if zoneType$ = "Clock" then zoneHSM = newClockZoneHSM(bsp, zoneXML) else if zoneType$ = "BackgroundImage" then zoneHSM = newBackgroundImageZoneHSM(bsp, zoneXML) endif zoneHSM.CreateObjects = CreateObjects zoneHSM.CreateCommunicationObjects = CreateCommunicationObjects zoneHSM.CreateObjectsNeededForTransitionCommands = CreateObjectsNeededForTransitionCommands zoneHSM.CreateObjectForTransitionCommand = CreateObjectForTransitionCommand zoneHSM.CreateSerial = CreateSerial zoneHSM.CreateUDPSender = CreateUDPSender zoneHSM.InitializeZoneCommon = InitializeZoneCommon zoneHSM.LoadImageBuffers = LoadImageBuffers zoneHSM.AddImageBufferItem = AddImageBufferItem zoneHSM.language$ = globalVariables.language$ ' create and read playlist playlistXML = zoneXML.playlist numPlaylists% = playlistXML.Count() if numPlaylists% = 1 then zoneHSM.playlist = newPlaylist(bsp, zoneHSM, sign, playlistXML) endif zoneHSM.playbackActive = false return zoneHSM End Function Function newPlaylist(bsp As Object, zoneHSM As Object, sign As Object, playlistXML As Object) As Object playlistBS = CreateObject("roAssociativeArray") playlistBS.name$ = playlistXML.name.GetText() ' get states stateList = playlistXML.states.state if type(stateList) <> "roXMLList" then print "Invalid XML file - state list not found" : stop initialStateXML = playlistXML.states.initialState if type(initialStateXML) <> "roXMLList" then print "Invalid XML file - initial state not found" : stop initialStateName$ = initialStateXML.GetText() if zoneHSM.type$ = "Ticker" then zoneHSM.rssDataFeedItems = CreateObject("roArray", 2, true) for each state in stateList tickerItem = newTickerItem(bsp, zoneHSM, state) if tickerItem <> invalid then zoneHSM.rssDataFeedItems.push(tickerItem) endif next else zoneHSM.stateTable = CreateObject("roAssociativeArray") for each state in stateList bsState = newState(bsp, zoneHSM, sign, state, invalid) next ' find the initial state for the playlist for each stateName in zoneHSM.stateTable bsState = zoneHSM.stateTable[stateName] if bsState.id$ = initialStateName$ then playlistBS.firstState = GetInitialState(zoneHSM, bsState) exit for endif next ' find the initial states for each superstate allStates = CreateObject("roArray", 1, true) for each stateName in zoneHSM.stateTable allStates.push(stateName) next for each stateName in allStates bsState = zoneHSM.stateTable[stateName] if bsState.type$ = "superState" then initialStateName$ = bsState.initialStateName$ for each innerStateName in zoneHSM.stateTable innerState = zoneHSM.stateTable[innerStateName] if innerState.id$ = initialStateName$ then bsState.firstState = GetInitialState(zoneHSM, innerState) exit for endif next endif next ' get transitions transitionList = playlistXML.states.transition for each transition in transitionList newTransition(bsp, zoneHSM, sign, transition) next endif return playlistBS End Function Function GetInitialState(zoneHSM As Object, state As Object) As Object if state.type$ <> "superState" return state initialStateName$ = state.initialStateName$ for each stateName in zoneHSM.stateTable state = zoneHSM.stateTable[stateName] if state.id$ = initialStateName$ then return GetInitialState(zoneHSM, state) endif next End Function Function ConvertToByteArray(input$ As String) As Object inputSpec = CreateObject("roByteArray") ' convert serial$ into byte array byteString$ = StripLeadingSpaces(input$) commaPosition = -1 while commaPosition <> 0 commaPosition = instr(1, byteString$, ",") if commaPosition = 0 then byteValue = val(byteString$) else byteValue = val(left(byteString$, commaPosition - 1)) endif inputSpec.push(byteValue) byteString$ = mid(byteString$, commaPosition+1) end while return inputSpec End Function Sub newTransition(bsp As Object, zoneHSM As Object, sign As Object, transitionXML As Object) stateTable = zoneHSM.stateTable sourceMediaState$ = transitionXML.sourceMediaState.GetText() ' given the sourceMediaState, find the associated bsState bsState = stateTable.Lookup(sourceMediaState$) if type(bsState) <> "roAssociativeArray" then print "Media state specified in transition not found" : stop userEvent = transitionXML.userEvent if userEvent.Count() <> 1 then print "Invalid XML file - userEvent not found" : stop userEventName$ = userEvent.name.GetText() nextMediaState$ = transitionXML.targetMediaState.GetText() transition = CreateObject("roAssociativeArray") transition.AssignEventInputToUserVariable = AssignEventInputToUserVariable transition.AssignWildcardInputToUserVariable = AssignWildcardInputToUserVariable transition.targetMediaState$ = nextMediaState$ ' if the transition points to a superstate, point it to the first state for the superstate instead if transition.targetMediaState$ <> "" then targetState = zoneHSM.stateTable[transition.targetMediaState$] if targetState.type$ = "superState" and targetState.firstState <> invalid then transition.targetMediaState$ = targetState.firstState.id$ endif endif nextIsPrevious$ = transitionXML.targetIsPreviousState.GetText() transition.targetMediaStateIsPreviousState = false if nextIsPrevious$ <> "" and lcase(nextIsPrevious$) = "yes" then transition.targetMediaStateIsPreviousState = true endif transition.assignInputToUserVariable = false if lcase(transitionXML.assignInputToUserVariable.GetText()) = "true" then transition.assignInputToUserVariable = true transition.variableToAssign = invalid variableToAssign$ = transitionXML.variableToAssign.GetText() if variableToAssign$ <> "" then transition.variableToAssign = bsp.GetUserVariable(variableToAssign$) if transition.variableToAssign = invalid then bsp.diagnostics.PrintDebug("User variable " + variableToAssign$ + " not found.") endif endif endif transition.assignWildcardToUserVariable = false if lcase(transitionXML.assignWildcardToUserVariable.GetText()) = "true" then transition.assignWildcardToUserVariable = true transition.variableToAssignFromWildcard = invalid variableToAssign$ = transitionXML.variableToAssignFromWildcard.GetText() if variableToAssign$ <> "" then transition.variableToAssignFromWildcard = bsp.GetUserVariable(variableToAssign$) if transition.variableToAssignFromWildcard = invalid then bsp.diagnostics.PrintDebug("User variable " + variableToAssign$ + " not found.") endif endif endif if userEventName$ = "gpioUserEvent" then transition.configuration$ = "press" if userEvent.parameters.buttonNumber.GetText() <> "" then buttonNumber$ = userEvent.parameters.buttonNumber.GetText() continuousConfigs = userEvent.parameters.GetNamedElements("pressContinuous") if continuousConfigs.Count() = 1 then continuousConfig = continuousConfigs[0] transition.configuration$ = "pressContinuous" transition.initialHoldoff$ = continuousConfig.initialHoldoff.GetText() transition.repeatInterval$ = continuousConfig.repeatInterval.GetText() endif else buttonNumber$ = userEvent.parameters.parameter.GetText() endif buttonDirection$ = "down" if userEvent.parameters.buttonDirection.GetText() <> "" then buttonDirection$ = userEvent.parameters.buttonDirection.GetText() endif bsp.ConfigureGPIOInput(buttonNumber$) if buttonDirection$ = "down" then gpioEvents = bsState.gpioEvents gpioEvents[buttonNumber$] = transition else gpioUpEvents = bsState.gpioUpEvents gpioUpEvents[buttonNumber$] = transition endif else if userEventName$ = "bp900AUserEvent" or userEventName$ = "bp900BUserEvent" or userEventName$ = "bp900CUserEvent" or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" then transition.configuration$ = "press" buttonPanelIndex% = int(val(userEvent.parameters.buttonPanelIndex.GetText())) buttonNumber$ = userEvent.parameters.buttonNumber.GetText() continuousConfigs = userEvent.parameters.GetNamedElements("pressContinuous") if continuousConfigs.Count() = 1 then continuousConfig = continuousConfigs[0] transition.configuration$ = "pressContinuous" transition.initialHoldoff$ = continuousConfig.initialHoldoff.GetText() transition.repeatInterval$ = continuousConfig.repeatInterval.GetText() endif bsp.ConfigureBPInput(buttonPanelIndex%, buttonNumber$) bpEvents = bsState.bpEvents currentBPEvent = bpEvents[buttonPanelIndex%] currentBPEvent.AddReplace(buttonNumber$, transition) else if userEventName$ = "gpsEvent" then enterRegion$ = userEvent.parameters.enterRegion.GetText() transition.radiusInFeet = val(userEvent.parameters.gpsRegion.radiusInFeet.GetText()) transition.latitude = val(userEvent.parameters.gpsRegion.latitude.GetText()) transition.latitudeInRadians = ConvertDecimalDegtoRad(transition.latitude) transition.longitude = val(userEvent.parameters.gpsRegion.longitude.GetText()) transition.longitudeInRadians = ConvertDecimalDegtoRad(transition.longitude) if lcase(enterRegion$) = "true" then bsState.gpsEnterRegionEvents.push(transition) else bsState.gpsExitRegionEvents.push(transition) endif else if userEventName$ = "serial" then ' support both old style and new style serial events if userEvent.parameters.parameter2.Count() = 1 then port$ = userEvent.parameters.parameter.GetText() serial$ = userEvent.parameters.parameter2.GetText() else port$ = "0" serial$ = userEvent.parameters.parameter.GetText() endif port% = int(val(port$)) serialPortConfiguration = sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ serialEvents = bsState.serialEvents if type(serialEvents[port$]) <> "roAssociativeArray" then serialEvents[port$] = CreateObject("roAssociativeArray") endif if protocol$ = "Binary" then if type(serialEvents[port$].streamInputTransitionSpecs) <> "roArray" then serialEvents[port$].streamInputTransitionSpecs = CreateObject("roArray", 1, true) endif streamInputTransitionSpec = CreateObject("roAssociativeArray") streamInputTransitionSpec.transition = transition streamInputTransitionSpec.inputSpec = ConvertToByteArray(serial$) streamInputTransitionSpec.asciiSpec = serial$ serialEvents[port$].streamInputTransitionSpecs.push(streamInputTransitionSpec) else serialPortEvents = serialEvents[port$] serialPortEvents[serial$] = transition endif else if userEventName$ = "usbBinaryEtap" then usbBinaryEtap$ = userEvent.parameters.parameter.GetText() usbBinaryEtapInputTransitionSpec = CreateObject("roAssociativeArray") usbBinaryEtapInputTransitionSpec.transition = transition usbBinaryEtapInputTransitionSpec.inputSpec = ConvertToByteArray(usbBinaryEtap$) usbBinaryEtapInputTransitionSpec.asciiSpec = usbBinaryEtap$ if type(bsState.usbBinaryEtapEvents) <> "roArray" then bsState.usbBinaryEtapEvents = CreateObject("roArray", 1, true) endif bsState.usbBinaryEtapEvents.push(usbBinaryEtapInputTransitionSpec) else if userEventName$ = "timeout" then bsState.mstimeoutValue% = int(val(userEvent.parameters.parameter.GetText()) * 1000) bsState.mstimeoutEvent = transition else if userEventName$ = "timeClockEvent" then timeClockEventTransitionSpec = { } timeClockEventTransitionSpec.transition = transition if type(userEvent.timeClockEvent.timeClockDateTime.GetChildElements()) = "roXMLList" then dateTime$ = userEvent.timeClockEvent.timeClockDateTime.dateTime.GetText() timeClockEventTransitionSpec.timeClockEventDateTime = FixDateTime(dateTime$) else if type(userEvent.timeClockEvent.timeClockDateTimeByUserVariable.GetChildElements()) = "roXMLList" then userVariableName$ = userEvent.timeClockEvent.timeClockDateTimeByUserVariable.userVariableName.GetText() timeClockEventTransitionSpec.userVariableName$ = userVariableName$ timeClockEventTransitionSpec.userVariable = bsp.GetUserVariable(userVariableName$) else timeClockEventTransitionSpec.daysOfWeek% = int(val(userEvent.timeClockEvent.timeClockDailyOnce.daysOfWeek.GetText())) if type(userEvent.timeClockEvent.timeClockDailyOnce.GetChildElements()) = "roXMLList" then timeClockEventTransitionSpec.timeClockDaily% = int(val(userEvent.timeClockEvent.timeClockDailyOnce.eventTime.GetText())) else timeClockEventTransitionSpec.daysOfWeek% = int(val(userEvent.timeClockEvent.timeClockDailyPeriodic.daysOfWeek.GetText())) timeClockEventTransitionSpec.timeClockPeriodicInterval% = int(val(userEvent.timeClockEvent.timeClockDailyPeriodic.intervalTime.GetText())) timeClockEventTransitionSpec.timeClockPeriodicStartTime% = int(val(userEvent.timeClockEvent.timeClockDailyPeriodic.startTime.GetText())) timeClockEventTransitionSpec.timeClockPeriodicEndTime% = int(val(userEvent.timeClockEvent.timeClockDailyPeriodic.endTime.GetText())) endif endif if type(bsState.timeClockEvents) <> "roArray" then bsState.timeClockEvents = CreateObject("roArray", 1, true) endif bsState.timeClockEvents.push(timeClockEventTransitionSpec) else if userEventName$ = "mediaEnd" then if bsState.type$ = "video" then bsState.videoEndEvent = transition else if bsState.type$ = "audio" then bsState.audioEndEvent = transition else if bsState.type$ = "signChannel" or bsState.type$ = "mediaRSS" then bsState.signChannelEndEvent = transition else if bsState.type$ = "mediaList" and bsState.mediaType$ = "video" then bsState.videoEndEvent = transition else if bsState.type$ = "mediaList" and bsState.mediaType$ = "audio" then bsState.audioEndEvent = transition else if bsState.type$ = "playFile" then bsState.videoEndEvent = transition bsState.audioEndEvent = transition else if bsState.type$ = "stream" then bsState.videoEndEvent = transition bsState.audioEndEvent = transition else if bsState.type$ = "mjpeg" then bsState.videoEndEvent = transition else if bsState.type$ = "rfInputChannel" then bsState.videoEndEvent = transition else if bsState.type$ = "rfScan" then bsState.videoEndEvent = transition else if bsState.type$ = "superState" then bsState.mediaEndEvent = transition endif else if userEventName$ = "keyboard" then keyboardChar$ = userEvent.parameters.parameter.GetText() if len(keyboardChar$) > 1 then keyboardChar$ = Lcase(keyboardChar$) endif if type(bsState.keyboardEvents) <> "roAssociativeArray" then bsState.keyboardEvents = CreateObject("roAssociativeArray") endif bsState.keyboardEvents[keyboardChar$] = transition else if userEventName$ = "remote" then remote$ = ucase(userEvent.parameters.parameter.GetText()) if type(bsState.remoteEvents) <> "roAssociativeArray" then bsState.remoteEvents = CreateObject("roAssociativeArray") endif bsState.remoteEvents[remote$] = transition else if userEventName$ = "usb" then usbString$ = userEvent.parameters.parameter.GetText() if type(bsState.usbStringEvents) <> "roAssociativeArray" then bsState.usbStringEvents = CreateObject("roAssociativeArray") endif bsState.usbStringEvents[usbString$] = transition else if userEventName$ = "udp" then udp$ = userEvent.parameters.parameter.GetText() if type(bsState.udpEvents) <> "roAssociativeArray" then bsState.udpEvents = CreateObject("roAssociativeArray") endif label$ = userEvent.parameters.label.GetText() export$ = LCase(userEvent.parameters.export.GetText()) transition.udpLabel$ = label$ if export$ = "true" then transition.udpExport = true else transition.udpExport = false endif bsState.udpEvents[udp$] = transition else if userEventName$ = "synchronize" then bsp.IsSyncSlave = true synchronize$ = userEvent.parameters.parameter.GetText() if type(bsState.synchronizeEvents) <> "roAssociativeArray" then bsState.synchronizeEvents = CreateObject("roAssociativeArray") endif bsState.synchronizeEvents[synchronize$] = transition else if userEventName$ = "zoneMessage" then zoneMessage$ = userEvent.parameters.parameter.GetText() if type(bsState.zoneMessageEvents) <> "roAssociativeArray" then bsState.zoneMessageEvents = CreateObject("roAssociativeArray") endif bsState.zoneMessageEvents[zoneMessage$] = transition else if userEventName$ = "pluginMessageEvent" then pluginName$ = userEvent.parameters.name.GetText() pluginMessage$ = userEvent.parameters.message.GetText() ' unique key is concatenation of plugin name and plugin message key$ = pluginName$ + pluginMessage$ if type(bsState.pluginMessageEvents) <> "roAssociativeArray" then bsState.pluginMessageEvents = CreateObject("roAssociativeArray") endif bsState.pluginMessageEvents[key$] = transition else if userEventName$ = "internalSynchronize" then internalSynchronize$ = userEvent.parameters.parameter.GetText() if type(bsState.internalSynchronizeEvents) <> "roAssociativeArray" then bsState.internalSynchronizeEvents = CreateObject("roAssociativeArray") endif bsState.internalSynchronizeEvents[internalSynchronize$] = transition else if userEventName$ = "rectangularTouchEvent" then if type(bsState.touchEvents) <> "roAssociativeArray" then bsState.touchEvents = CreateObject("roAssociativeArray") endif transition.x% = int(val(userEvent.parameters.x.GetText())) transition.y% = int(val(userEvent.parameters.y.GetText())) transition.width% = int(val(userEvent.parameters.width.GetText())) transition.height% = int(val(userEvent.parameters.height.GetText())) if sign.flipCoordinates then videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode = invalid transition.x% = resX - (transition.x% + transition.width%) transition.y% = resY - (transition.y% + transition.height%) endif bsState.touchEvents[stri(sign.numTouchEvents%)] = transition sign.numTouchEvents% = sign.numTouchEvents% + 1 else if userEventName$ = "videoTimeCodeEvent" then if type(bsState.videoTimeCodeEvents) <> "roAssociativeArray" then bsState.videoTimeCodeEvents = CreateObject("roAssociativeArray") endif transition.timeInMS% = int(val(userEvent.parameters.parameter.GetText())) bsState.videoTimeCodeEvents[stri(sign.numVideoTimeCodeEvents%)] = transition sign.numVideoTimeCodeEvents% = sign.numVideoTimeCodeEvents% + 1 else if userEventName$ = "quietUserEvent" then bsState.quietUserEvent = transition else if userEventName$ = "loudUserEvent" then bsState.loudUserEvent = transition else if userEventName$ = "auxConnectUserEvent" then if type(bsState.auxConnectEvents) <> "roAssociativeArray" then bsState.auxConnectEvents = CreateObject("roAssociativeArray") endif audioConnector$ = userEvent.parameters.audioConnector.GetText() bsState.auxConnectEvents[audioConnector$] = transition else if userEventName$ = "auxDisconnectUserEvent" then if type(bsState.auxDisconnectEvents) <> "roAssociativeArray" then bsState.auxDisconnectEvents = CreateObject("roAssociativeArray") endif audioConnector$ = userEvent.parameters.audioConnector.GetText() bsState.auxDisconnectEvents[audioConnector$] = transition else if userEventName$ = "success" then bsState.successEvent = transition else if userEventName$ = "fail" then bsState.failEvent = transition endif ' get commands and conditional targets for each transitionItemXML in transitionXML.GetChildElements() if transitionItemXML.GetName() = "brightSignCmd" then if type(transition.transitionCmds) <> "roArray" then transition.transitionCmds = CreateObject("roArray", 1, true) endif newCmd(bsp, transitionItemXML, transition.transitionCmds) endif if transitionItemXML.GetName() = "conditionalTarget" then if type(transition.conditionalTargets) <> "roArray" then transition.conditionalTargets = CreateObject("roArray", 1, true) endif newConditionalTarget(bsp, transitionItemXML, transition.conditionalTargets) endif next for each transitionCmd in transition.transitionCmds ' if the transition command is for an internal synchronize, add an event that the master will receive after it sends the preload command if transitionCmd.name$ = "internalSynchronize" then if type(transition.internalSynchronizeEventsMaster) <> "roAssociativeArray" then transition.internalSynchronizeEventsMaster = CreateObject("roAssociativeArray") endif internalSynchronizeMasterTransition = CreateObject("roAssociativeArray") internalSynchronizeMasterTransition.targetMediaState$ = nextMediaState$ internalSynchronizeMasterTransition.targetMediaStateIsPreviousState = false transition.internalSynchronizeEventsMaster[transitionCmd.parameters["synchronizeKeyword"].GetCurrentParameterValue()] = internalSynchronizeMasterTransition ' modify this state's transition to not go to the next media state transition.targetMediaState$ = "" endif next End Sub Function newConditionalTarget(bsp As Object, conditionalTargetXML As Object, conditionalTargets As Object) userVariableName$ = conditionalTargetXML.variableName.GetText() operator$ = conditionalTargetXML.operator.GetText() if operator$ = "" then operator$ = "EQ" targetMediaState$ = conditionalTargetXML.targetMediaState.GetText() nextIsPrevious$ = conditionalTargetXML.targetIsPreviousState.GetText() if conditionalTargetXML.variableValue.GetText() <> "" then userVariableValue$ = conditionalTargetXML.variableValue.GetText() userVariableValue = newTextParameterValue(userVariableValue$) else userVariableValue = newParameterValue(bsp, conditionalTargetXML.variableValueSpec.parameterValue) endif if conditionalTargetXML.variableValue2.GetText() <> "" then userVariableValue2$ = conditionalTargetXML.variableValue2.GetText() userVariableValue2 = newTextParameterValue(userVariableValue2$) else userVariableValue2 = newParameterValue(bsp, conditionalTargetXML.variableValue2Spec.parameterValue) endif userVariable = bsp.GetUserVariable(userVariableName$) if type(userVariable) = "roAssociativeArray" then conditionalTarget = { } conditionalTarget.userVariable = userVariable conditionalTarget.operator$ = operator$ conditionalTarget.userVariableValue = userVariableValue conditionalTarget.userVariableValue2 = userVariableValue2 conditionalTarget.targetMediaState$ = targetMediaState$ conditionalTarget.targetMediaStateIsPreviousState = false if nextIsPrevious$ <> "" and lcase(nextIsPrevious$) = "yes" then conditionalTarget.targetMediaStateIsPreviousState = true endif brightSignCmdsXML = conditionalTargetXML.brightSignCmd if type(brightSignCmdsXML) = "roXMLList" and brightSignCmdsXML.Count() > 0 then conditionalTarget.transitionCmds = CreateObject("roArray", 1, true) for each brightSignCmdXML in brightSignCmdsXML newCmd(bsp, brightSignCmdXML, conditionalTarget.transitionCmds) next endif conditionalTargets.push(conditionalTarget) else bsp.diagnostics.PrintDebug("User variable " + userVariableName$ + " not found.") endif End Function Function newTickerItem(bsp As Object, zoneHSM As Object, stateXML As Object) As Object item = invalid if stateXML.rssItem.Count() = 1 then item = newRSSPlaylistItem(bsp, zoneHSM, stateXML.rssItem) else if stateXML.twitterItem.Count() = 1 then item = newTwitterPlaylistItem(bsp, zoneHSM, stateXML.twitterItem) else if stateXML.rssDataFeedPlaylistItem.Count() = 1 then item = newRSSDataFeedPlaylistItem(bsp, stateXML.rssDataFeedPlaylistItem) else if stateXML.textItem.Count() = 1 then item = newTextPlaylistItem(stateXML.textItem) endif return item End Function Function newState(bsp As Object, zoneHSM As Object, sign As Object, stateXML As Object, superState As Object) As Object ' get the name stateName$ = stateXML.name.GetText() state = zoneHSM.newHState(bsp, stateName$) state.name$ = stateName$ if type(superState) = "roAssociativeArray" then state.superState = superState else state.superState = zoneHSM.stTop endif ' create data structures for arrays of specific events state.gpioEvents = CreateObject("roAssociativeArray") state.gpioUpEvents = CreateObject("roAssociativeArray") state.bpEvents = CreateObject("roArray", 3, true) state.bpEvents[0] = CreateObject("roAssociativeArray") state.bpEvents[1] = CreateObject("roAssociativeArray") state.bpEvents[2] = CreateObject("roAssociativeArray") state.serialEvents = CreateObject("roAssociativeArray") state.gpsEnterRegionEvents = CreateObject("roArray", 1, true) state.gpsExitRegionEvents = CreateObject("roArray", 1, true) ' get the item item = CreateObject("roAssociativeArray") if stateXML.imageItem.Count() = 1 then newImagePlaylistItem(bsp, stateXML.imageItem, state, item) state.imageItem = item state.type$ = "image" zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 else if stateXML.videoItem.Count() = 1 then newVideoPlaylistItem(bsp, stateXML.videoItem, state, item) state.videoItem = item state.type$ = "video" else if stateXML.liveVideoItem.Count() = 1 then newLiveVideoPlaylistItem(stateXML.liveVideoItem, state) state.type$ = "liveVideo" else if stateXML.rfInputItem.Count() = 1 then newRFInputPlaylistItem(bsp, stateXML.rfInputItem, state) state.type$ = "rfInputChannel" else if stateXML.rfScanItem.Count() = 1 then newRFScanPlaylistItem(stateXML.rfScanItem, state) state.type$ = "rfScan" else if stateXML.eventHandlerItem.Count() = 1 then newEventHandlerPlaylistItem(stateXML.eventHandlerItem, state) state.type$ = "eventHandler" else if stateXML.eventHandler2Item.Count() = 1 then newEventHandlerPlaylistItem(stateXML.eventHandler2Item, state) state.type$ = "eventHandler" else if stateXML.liveTextItem.Count() = 1 then newTemplatePlaylistItemFromLiveTextPlaylistItem(bsp, stateXML.liveTextItem, state) state.type$ = "template" else if stateXML.templatePlaylistItem.Count() = 1 then newTemplatePlaylistItem(bsp, stateXML.templatePlaylistItem, state) state.type$ = "template" else if stateXML.audioInItem.Count() = 1 then newAudioInPlaylistItem(bsp, stateXML.audioInItem, state) state.type$ = "audioIn" if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.signChannelItem.Count() = 1 then ' require that the storage is writable if bsp.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() newSignChannelPlaylistItem(bsp, stateXML.signChannelItem, state) state.type$ = "signChannel" if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.mrssDataFeedPlaylistItem.Count() = 1 or stateXML.rssImageItem.Count() = 1 then ' require that the storage is writable if bsp.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() if stateXML.mrssDataFeedPlaylistItem.Count() = 1 then newMRSSPlaylistItem(bsp, stateXML.mrssDataFeedPlaylistItem, state) else newRSSImagePlaylistItem(bsp, stateXML.rssImageItem, state) endif state.type$ = "mediaRSS" if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.localPlaylistItem.Count() = 1 then newLocalPlaylistItem(bsp, stateXML.localPlaylistItem, state) state.type$ = "mediaRSS" if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.audioItem.Count() = 1 then newAudioPlaylistItem(bsp, stateXML.audioItem, state, item) state.audioItem = item state.type$ = "audio" else if stateXML.mediaSuperState.Count() = 1 then state.HStateEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames else if stateXML.backgroundImageItem.Count() = 1 then newBackgroundImagePlaylistItem(bsp, stateXML.backgroundImageItem, state, item) state.backgroundImageItem = item else if stateXML.mediaListItem.Count() = 1 then newMediaListPlaylistItem(bsp, zoneHSM, stateXML.mediaListItem, state) state.type$ = "mediaList" else if stateXML.tripleUSBItem.Count() = 1 then newTripleUSBPlaylistItem(stateXML.tripleUSBItem, sign, state) state.type$ = "tripleUSB" else if stateXML.interactiveMenuItem.Count() = 1 then newInteractiveMenuPlaylistItem(bsp, sign, stateXML.interactiveMenuItem, state) state.type$ = "interactiveMenuItem" if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.playFileItem.Count() = 1 then newPlayFilePlaylistItem(bsp, stateXML.playFileItem, state) state.type$ = "playFile" if stateXML.playFileItem.mediaType.GetText() = "image" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif else if stateXML.streamItem.Count() = 1 then newStreamPlaylistItem(bsp, stateXML.streamItem, state) state.mediaType$ = "video" state.type$ = "stream" else if stateXML.videoStreamItem.Count() = 1 then newStreamPlaylistItem(bsp, stateXML.videoStreamItem, state) state.mediaType$ = "video" state.type$ = "stream" else if stateXML.audioStreamItem.Count() = 1 then newStreamPlaylistItem(bsp, stateXML.audioStreamItem, state) state.mediaType$ = "audio" state.type$ = "stream" else if stateXML.mjpegItem.Count() = 1 then newMjpegStreamPlaylistItem(bsp, stateXML.mjpegItem, state) state.type$ = "mjpeg" else if stateXML.html5Item.Count() = 1 then newHtml5PlaylistItem(bsp, stateXML.html5Item, state) state.type$ = "html5" else if stateXML.xModemItem.Count() = 1 then newXModemPlaylistItem(stateXML.xModemItem, state) state.type$ = "xModem" else if stateXML.superStateItem.Count() = 1 then newSuperStateItem(bsp, zoneHSM, sign, stateXML.superStateItem, state) state.type$ = "superState" endif ' get any media state commands (commands that are executed when a state is entered) state.cmds = CreateObject("roArray", 1, true) ' new style commands cmds = stateXML.brightSignCmd if stateXML.brightSignCmd.Count() > 0 then for each cmd in cmds newCmd(bsp, cmd, state.cmds) next endif ' get media state exit commands state.exitCmds = CreateObject("roArray", 1, true) exitCmds = stateXML.brightSignExitCommands.brightSignCmd if stateXML.brightSignExitCommands.brightSignCmd.Count() > 0 then for each cmd in exitCmds newCmd(bsp, cmd, state.exitCmds) next endif zoneHSM.stateTable[state.id$] = state return state End Function Function newTextParameterValue(value$ As String) As Object parameterValue = CreateObject("roAssociativeArray") parameterValue.GetCurrentParameterValue = GetCurrentParameterValue parameterValue.GetParameterValueSpec = GetParameterValueSpec parameterValue.parameterValueItems = CreateObject("roArray", 1, true) parameterValue.parameterValueItems.push(newParameterValueItemFromTextConstant(value$)) return parameterValue End Function Function GetCurrentTextParameterValue() As String return m.textValue$ End Function Function GetParameterValueSpecItemText() As String return m.textValue$ End Function Function newParameterValueItemText(parameterValueItemTextXML As Object) As Object parameterValueItem = CreateObject("roAssociativeArray") parameterValueItem.GetCurrentValue = GetCurrentTextParameterValue parameterValueItem.GetParameterValueSpec = GetParameterValueSpecItemText parameterValueItem.type$ = "text" parameterValueItem.textValue$ = parameterValueItemTextXML.value.GetText() return parameterValueItem End Function Function newParameterValueItemFromTextConstant(textValue$ As String) As Object parameterValueItem = CreateObject("roAssociativeArray") parameterValueItem.GetCurrentValue = GetCurrentTextParameterValue parameterValueItem.GetParameterValueSpec = GetParameterValueSpecItemText parameterValueItem.type$ = "text" parameterValueItem.textValue$ = textValue$ return parameterValueItem End Function Function GetCurrentUserVariableParameterValue() As String if type(m.userVariable) = "roAssociativeArray" then return m.userVariable.GetCurrentValue() else return "" endif End Function Function GetParameterValueSpecItemUserVariable() As String if type(m.userVariable) = "roAssociativeArray" then return "$$" + m.userVariable.name$ + "$$" else return "" endif End Function Function newParameterValueItemUserVariable(bsp As Object, parameterValueItemUserVariableXML As Object) As Object parameterValueItem = CreateObject("roAssociativeArray") parameterValueItem.GetCurrentValue = GetCurrentUserVariableParameterValue parameterValueItem.GetParameterValueSpec = GetParameterValueSpecItemUserVariable parameterValueItem.type$ = "userVariable" userVariableName$ = parameterValueItemUserVariableXML.userVariable.name.GetText() parameterValueItem.userVariable = bsp.GetUserVariable(userVariableName$) if type(parameterValueItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("User variable " + userVariableName$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, userVariableName$) endif return parameterValueItem End Function Function GetParameterValueSpecItemMediaCounterVariable() As String if type(m.userVariable) = "roAssociativeArray" then return "_" + m.userVariable.name$ else return "" endif End Function Function newParameterValueItemMediaCounterVariable(bsp As Object, parameterValueItemMediaCounterVariable As Object) As Object parameterValueItem = CreateObject("roAssociativeArray") parameterValueItem.GetCurrentValue = GetCurrentUserVariableParameterValue parameterValueItem.GetParameterValueSpec = GetParameterValueSpecItemMediaCounterVariable parameterValueItem.type$ = "userVariable" variableName$ = parameterValueItemMediaCounterVariable.fileName.GetText() userVariableName$ = mid(variableName$, 2) parameterValueItem.userVariable = bsp.GetUserVariable(userVariableName$) if type(parameterValueItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("Media counter variable " + userVariableName$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_MEDIA_COUNTER_VARIABLE_NOT_FOUND, userVariableName$) endif return parameterValueItem End Function Function GetCurrentParameterValue() As String value$ = "" for each parameterValueItem in m.parameterValueItems if type(parameterValueItem) = "roAssociativeArray" then value$ = value$ + parameterValueItem.GetCurrentValue() endif next return value$ End Function Function GetVariableName() As String variableName$ = "" if m.parameterValueItems.Count() = 1 then parameterValueItem = m.parameterValueItems[0] if type(parameterValueItem) = "roAssociativeArray" then if parameterValueItem.type$ = "userVariable" then userVariable = parameterValueItem.userVariable variableName$ = userVariable.name$ endif endif endif return variableName$ End Function Function GetParameterValueSpec() As String parameterValueSpec$ = "" for each parameterValueItem in m.parameterValueItems parameterValueSpec$ = parameterValueSpec$ + parameterValueItem.GetParameterValueSpec() next return parameterValueSpec$ End Function Function newParameterValue(bsp As Object, parameterValueXML As Object) As Object parameterValue = CreateObject("roAssociativeArray") parameterValue.GetCurrentParameterValue = GetCurrentParameterValue parameterValue.GetVariableName = GetVariableName parameterValue.GetParameterValueSpec = GetParameterValueSpec parameterValue.parameterValueItems = CreateObject("roArray", 1, true) if type(parameterValueXML) = "roXMLList" and parameterValueXML.Count() = 1 then parameterValueItemsXML = parameterValueXML.GetChildElements() for each parameterValueItemXML in parameterValueItemsXML if parameterValueItemXML.GetName() = "parameterValueItemText" then parameterValue.parameterValueItems.push(newParameterValueItemText(parameterValueItemXML)) else if parameterValueItemXML.GetName() = "parameterValueItemUserVariable" then parameterValue.parameterValueItems.push(newParameterValueItemUserVariable(bsp, parameterValueItemXML)) else if parameterValueItemXML.GetName() = "parameterValueItemMediaCounterVariable" then parameterValue.parameterValueItems.push(newParameterValueItemMediaCounterVariable(bsp, parameterValueItemXML)) endif next endif return parameterValue End Function Sub newCmd(bsp As Object, cmdXML As Object, cmds As Object) numCmds% = cmdXML.command.Count() if numCmds% > 0 then cmdsXML = cmdXML.command for each cmd in cmdsXML bsCmd = CreateObject("roAssociativeArray") bsCmd.name$ = cmd.name.GetText() bsCmd.parameters = CreateObject("roAssociativeArray") numParameters% = cmd.parameter.Count() if numParameters% > 0 then parameters = cmd.parameter for each parameter in parameters if type(parameter.value) = "roXMLList" and parameter.value.Count() = 1 then value$ = parameter.value.GetText() parameterValue = newTextParameterValue(value$) else parameterValue = newParameterValue(bsp, parameter.parameterValue) endif bsCmd.parameters.AddReplace(parameter.name.GetText(), parameterValue) next endif cmds.push(bsCmd) if bsCmd.name$ = "sendBPOutput" then buttonNumber$ = bsCmd.parameters.buttonNumber.GetCurrentParameterValue() buttonPanelIndex% = int(val(bsCmd.parameters.buttonPanelIndex.GetCurrentParameterValue())) action$ = bsCmd.parameters.action.GetCurrentParameterValue() if buttonNumber$ = "-1" then if action$ <> "off" then for i% = 0 to 10 bsp.bpOutputUsed[buttonPanelIndex%, i%] = true next endif else buttonNumber% = int(val(buttonNumber$)) bsp.bpOutputUsed[buttonPanelIndex%, buttonNumber%] = true endif else if bsCmd.name$ = "switchPresentation" then ' required for compatibility with old published presentations presentationName$ = bsCmd.parameters.presentationName.GetCurrentParameterValue() ' new format has > 1 parameters: presentationName and useUserVariable if numParameters% = 1 then presentation = {} presentation.name$ = presentationName$ presentation.presentationName$ = presentationName$ presentation.path$ = presentationName$ bsp.presentations.AddReplace(presentation.name$, presentation) endif endif next endif End Sub Sub UpdateWidgetVisibility(showImage As Boolean, hideImage As Boolean, clearImage As Boolean, showCanvas As Boolean, hideCanvas As Boolean, showHtml As Boolean, hideHtml As Boolean) if hideImage then if type(m.imagePlayer) = "roImageWidget" then m.imagePlayer.Hide() endif endif if clearImage then if type(m.imagePlayer) = "roImageWidget" then m.imagePlayer.StopDisplay() endif endif if hideCanvas then if type(m.canvasWidget) = "roCanvasWidget" then m.canvasWidget.Hide() endif endif if hideHtml then if type(m.displayedHtmlWidget) = "roHtmlWidget" then ' m.displayedHtmlWidget.Hide() m.loadingHtmlWidget = invalid m.displayedHtmlWidget = invalid endif endif if showImage then if type(m.imagePlayer) = "roImageWidget" then m.imagePlayer.Show() endif endif if showCanvas then if type(m.canvasWidget) = "roCanvasWidget" then m.canvasWidget.Show() endif endif if showHtml then if type(m.displayedHtmlWidget) = "roHtmlWidget" then m.displayedHtmlWidget.Show() endif endif End Sub Sub ShowImageWidget() m.UpdateWidgetVisibility(true, false, false, false, true, false, true) End Sub Sub ClearImagePlane() m.UpdateWidgetVisibility(false, false, true, false, true, false, true) End Sub Sub ShowCanvasWidget() m.UpdateWidgetVisibility(false, true, false, true, false, false, true) End Sub Sub ShowHtmlWidget() m.UpdateWidgetVisibility(false, true, true, false, true, true, false) End Sub Sub LogPlayStart(itemType$ As String, fileName$ As String) if m.playbackActive then m.playbackEndTime$ = m.bsp.systemTime.GetLocalDateTime().GetString() m.bsp.logging.WritePlaybackLogEntry(m.name$, m.playbackStartTime$, m.playbackEndTime$, m.playbackItemType$, m.playbackFileName$) endif m.playbackActive = true m.playbackStartTime$ = m.bsp.systemTime.GetLocalDateTime().GetString() m.playbackItemType$ = itemType$ m.playbackFileName$ = fileName$ End Sub Sub newMediaPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object, playlistItemBS As Object) file = playlistItemXML.file fileAttrs = file.GetAttributes() playlistItemBS.fileName$ = fileAttrs["name"] playlistItemBS.userVariable = bsp.GetUserVariable(playlistItemBS.fileName$) state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.PreloadItem = PreloadItem End Sub Sub newImagePlaylistItem(bsp As Object, playlistItemXML As Object, state As Object, playlistItemBS As Object) newMediaPlaylistItem(bsp, playlistItemXML, state, playlistItemBS) playlistItemBS.slideDelayInterval% = int(val(playlistItemXML.slideDelayInterval.GetText())) playlistItemBS.slideTransition% = GetSlideTransitionValue(playlistItemXML.slideTransition.GetText()) playlistItemBS.useImageBuffer = false useImageBuffer$ = playlistItemXML.useImageBuffer.GetText() if len(useImageBuffer$) > 0 then useImageBuffer$ = lcase(useImageBuffer$) if useImageBuffer$ = "true" then playlistItemBS.useImageBuffer = true endif endif state.HStateEventHandler = STDisplayingImageEventHandler state.DisplayImage = DisplayImage state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Function GetProbeData(assetPoolFiles As Object, fileName$ As String) As Object probe = invalid poolFileInfo = assetPoolFiles.GetPoolFileInfo(fileName$) if type(poolFileInfo) = "roAssociativeArray" then probe = poolFileInfo.Lookup("probe") endif return probe End Function Sub newVideoPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object, playlistItemBS As Object) newMediaPlaylistItem(bsp, playlistItemXML, state, playlistItemBS) playlistItemBS.probeData = GetProbeData(bsp.assetPoolFiles, playlistItemBS.fileName$) itemVolume$ = playlistItemXML.volume.GetText() if itemVolume$ <> "" then playlistItemBS.volume% = int(val(itemVolume$)) endif playlistItemBS.videoDisplayMode% = 0 videoDisplayMode$ = playlistItemXML.videoDisplayMode.GetText() if videoDisplayMode$ = "3DSBS" then playlistItemBS.videoDisplayMode% = 1 else if videoDisplayMode$ = "3DTOB" then playlistItemBS.videoDisplayMode% = 2 endif state.HStateEventHandler = STVideoPlayingEventHandler state.AddVideoTimeCodeEvent = AddVideoTimeCodeEvent state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents state.LaunchVideo = LaunchVideo state.PrePlayVideo = PrePlayVideo state.PlayVideo = PlayVideo state.PostPlayVideo = PostPlayVideo state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Sub newSuperStateItem(bsp As Object, zoneHSM As Object, sign As Object, playlistItemXML As Object, state As Object) state.initialStateName$ = playlistItemXML.initialState.getText() subStatesXML = playlistItemXML.state for each subStateXML in subStatesXML subState = newState(bsp, zoneHSM, sign, subStateXML, state) next state.HStateEventHandler = STSuperStateEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.ConfigureBPButtons = ConfigureBPButtons End Sub Sub newEventHandlerPlaylistItem(playlistItemXML As Object, state As Object) state.stopPlayback = false stopPlayback$ = playlistItemXML.stopPlayback.getText() if lcase(stopPlayback$) = "true" then state.stopPlayback = true endif state.HStateEventHandler = STEventHandlerEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Sub newXModemPlaylistItem(playlistItemXML As Object, state As Object) state.name$ = playlistItemXML.name.GetText() file = playlistItemXML.file fileAttrs = file.GetAttributes() state.fileName$ = fileAttrs["name"] state.crc16$ = fileAttrs["crc16"] state.port$ = playlistItemXML.serialPort.GetText() state.numberOfRetries% = int(val(playlistItemXML.numberOfRetries.GetText())) state.HStateEventHandler = STXModemEventHandler state.SendXModemBlock = SendXModemBlock state.RestartXModemTimeoutTimer = RestartXModemTimeoutTimer state.RetryXModemTransfer = RetryXModemTransfer state.MediaItemEventHandler = MediaItemEventHandler state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchTimer = LaunchTimer state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames End Sub Sub newHtml5PlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.name$ = playlistItemXML.name.GetText() state.htmlSiteName$ = playlistItemXML.htmlSiteName.GetText() ' get the associated html site if bsp.htmlSites.DoesExist(state.htmlSiteName$) then htmlSite = bsp.htmlSites.Lookup(state.htmlSiteName$) state.contentIsLocal = htmlSite.contentIsLocal if state.contentIsLocal then state.prefix$ = htmlSite.prefix$ state.filePath$ = htmlSite.filePath$ state.url = invalid else state.url = htmlSite.url endif else ' what to do here? stop endif state.enableExternalData = false if lcase(playlistItemXML.enableExternalData.GetText()) = "true" then state.enableExternalData = true endif state.enableMouseEvents = false if lcase(playlistItemXML.enableMouseEvents.GetText()) = "true" then state.enableMouseEvents = true endif state.displayCursor = false if lcase(playlistItemXML.displayCursor.GetText()) = "true" then state.displayCursor = true endif state.customFonts = [] customFontsXML = playlistItemXML.customFont if type(customFontsXML) = "roXMLList" and customFontsXML.Count() > 0 then for each customFontXML in customFontsXML state.customFonts.push(customFontXML.GetText()) next endif state.HStateEventHandler = STHTML5PlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchTimer = LaunchTimer state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames End Sub Sub newRFScanPlaylistItem(playlistItemXML As Object, state As Object) state.scanSpec = { } channelMap$ = playlistItemXML.channelMap.GetText() if channelMap$ <> "ATSC_QAM" then state.scanSpec["ChannelMap"] = channelMap$ endif modulationType$ = playlistItemXML.modulationType.GetText() if channelMap$ <> "ATSC" and modulationType$ <> "QAM64_QAM256" then state.scanSpec["ModulationType"] = modulationType$ endif firstRFChannel$ = playlistItemXML.firstRFChannel.GetText() if firstRFChannel$ <> "" then state.scanSpec["FirstRfChannel"] = int(val(firstRFChannel$)) endif lastRFChannel$ = playlistItemXML.lastRFChannel.GetText() if lastRFChannel$ <> "" then state.scanSpec["LastRfChannel"] = int(val(lastRFChannel$)) endif state.HStateEventHandler = STRFScanHandler state.ProcessScannedChannels = ProcessScannedChannels state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.UpdateTunerScanPercentageComplete = UpdateTunerScanPercentageComplete End Sub Sub newRFInputPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.channelDescriptor = { } state.firstScannedChannel = false virtualChannel$ = playlistItemXML.rfInChannelDescriptor.rfInputChannel.virtualChannel.getText() ' user specified channel name if virtualChannel$ <> "" then state.channelDescriptor.VirtualChannel = virtualChannel$ else virtualChannel$ = playlistItemXML.rfInVirtualChannel.virtualChannel.getText() ' user specified virtual channel if virtualChannel$ <> "" then state.channelDescriptor.VirtualChannel = virtualChannel$ else userVariable$ = playlistItemXML.rfInUserVariable.userVariable.getText() ' user specified user variable if userVariable$ <> "" then state.userVariable = bsp.GetUserVariable(userVariable$) state.channelDescriptor = invalid else state.channelDescriptor = bsp.scannedChannels[0] ' user specified first scanned channel state.firstScannedChannel = true endif endif endif state.reentryAction$ = playlistItemXML.reentryAction.GetText() if type(playlistItemXML.channelUp) = "roXMLList" and playlistItemXML.channelUp.Count() = 1 then state.channelUpEvent = CreateObject("roAssociativeArray") SetStateEvent(bsp, state.channelUpEvent, playlistItemXML.channelUp) endif if type(playlistItemXML.channelDown) = "roXMLList" and playlistItemXML.channelDown.Count() = 1 then state.channelDownEvent = CreateObject("roAssociativeArray") SetStateEvent(bsp, state.channelDownEvent, playlistItemXML.channelDown) endif itemVolume$ = playlistItemXML.volume.GetText() if itemVolume$ <> "" then state.volume% = int(val(itemVolume$)) endif state.overscan = false if lcase(playlistItemXML.overscan.GetText()) = "true" then state.overscan = true endif state.HStateEventHandler = STRFInputPlayingHandler state.HandleIntraStateEvent = HandleIntraStateEvent state.ConfigureIntraStateEventHandlerButton = ConfigureIntraStateEventHandlerButton state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer End Sub Sub newLiveVideoPlaylistItem(playlistItemXML As Object, state As Object) itemVolume$ = playlistItemXML.volume.GetText() if itemVolume$ <> "" then state.volume% = int(val(itemVolume$)) endif state.overscan = false if lcase(playlistItemXML.overscan.GetText()) = "true" then state.overscan = true endif state.HStateEventHandler = STLiveVideoPlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer End Sub Sub newInteractiveMenuPlaylistItem(bsp As Object, sign As Object, playlistItemXML As Object, state As Object) state.backgroundImage$ = ParseFileNameXML(playlistItemXML.backgroundImage) if state.backgroundImage$ <> "" then state.backgroundImageUseImageBuffer = ParseBoolAttribute(playlistItemXML.backgroundImage[0], "useImageBuffer") endif state.backgroundImageUserVariable = bsp.GetUserVariable(state.backgroundImage$) state.upNavigation = ParseNavigation(bsp, sign, playlistItemXML.upNavigationEvent) state.downNavigation = ParseNavigation(bsp, sign, playlistItemXML.downNavigationEvent) state.leftNavigation = ParseNavigation(bsp, sign, playlistItemXML.leftNavigationEvent) state.rightNavigation = ParseNavigation(bsp, sign, playlistItemXML.rightNavigationEvent) state.enterNavigation = ParseNavigation(bsp, sign, playlistItemXML.enterNavigationEvent) state.backNavigation = ParseNavigation(bsp, sign, playlistItemXML.backNavigationEvent) state.nextClipNavigation = ParseNavigation(bsp, sign, playlistItemXML.nextClipNavigationEvent) state.previousClipNavigation = ParseNavigation(bsp, sign, playlistItemXML.previousClipNavigationEvent) interactiveMenuItemsXML = playlistItemXML.interactiveMenuItems.interactiveMenuItem state.interactiveMenuItems = CreateObject("roArray", 2, true) for each interactiveMenuItemXML in interactiveMenuItemsXML interactiveMenuItem = newInteractiveMenuItem(bsp, interactiveMenuItemXML) state.interactiveMenuItems.push(interactiveMenuItem) next state.HStateEventHandler = STInteractiveMenuEventHandler state.HandleInteractiveMenuEventInput = HandleInteractiveMenuEventInput state.DrawInteractiveMenu = DrawInteractiveMenu state.DisplayNavigationOverlay = DisplayNavigationOverlay state.NavigateToMenuItem = NavigateToMenuItem state.RestartInteractiveMenuInactivityTimer = RestartInteractiveMenuInactivityTimer state.ExecuteInteractiveMenuEnter = ExecuteInteractiveMenuEnter state.LaunchInteractiveMenuVideoClip = LaunchInteractiveMenuVideoClip state.LaunchInteractiveMenuAudioClip = LaunchInteractiveMenuAudioClip state.DisplayInteractiveMenuImage = DisplayInteractiveMenuImage state.NextPrevInteractiveMenuLaunchMedia = NextPrevInteractiveMenuLaunchMedia state.ConsumeSerialByteInput = ConsumeSerialByteInput state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.IsPlayingClip = IsPlayingClip state.ClearPlayingClip = ClearPlayingClip state.PreloadItem = PreloadItem state.ConfigureNavigationButton = ConfigureNavigationButton End Sub Function newInteractiveMenuItem(bsp As Object, interactiveMenuItemXML As Object) As Object interactiveMenuItem = CreateObject("roAssociativeArray") interactiveMenuItem.index$ = interactiveMenuItemXML.index.GetText() interactiveMenuItem.x% = int(val(interactiveMenuItemXML.x.GetText())) interactiveMenuItem.y% = int(val(interactiveMenuItemXML.y.GetText())) interactiveMenuItem.selectedImage$ = ParseFileNameXML(interactiveMenuItemXML.selectedImage) interactiveMenuItem.selectedImageUseImageBuffer = ParseBoolAttribute(interactiveMenuItemXML.selectedImage[0], "useImageBuffer") interactiveMenuItem.unselectedImage$ = ParseFileNameXML(interactiveMenuItemXML.unselectedImage) interactiveMenuItem.unselectedImageUseImageBuffer = ParseBoolAttribute(interactiveMenuItemXML.unselectedImage[0], "useImageBuffer") interactiveMenuItem.targetType$ = interactiveMenuItemXML.targetType.GetText() if interactiveMenuItem.targetType$ = "mediaFile" then interactiveMenuItem.targetVideoFile$ = ParseFileNameXML(interactiveMenuItemXML.targetVideoFile) interactiveMenuitem.targetVideoFileUserVariable = bsp.GetUserVariable(interactiveMenuItem.targetVideoFile$) interactiveMenuItem.targetAudioFile$ = ParseFileNameXML(interactiveMenuItemXML.targetAudioFile) interactiveMenuitem.targetAudioFileUserVariable = bsp.GetUserVariable(interactiveMenuItem.targetAudioFile$) interactiveMenuItem.targetImageFile$ = ParseFileNameXML(interactiveMenuItemXML.targetImageFile) interactiveMenuitem.targetImageFileUserVariable = bsp.GetUserVariable(interactiveMenuItem.targetImageFile$) if interactiveMenuItem.targetImageFile$ <> "" then element = interactiveMenuItemXML.targetImageFile[0] imageFileAttributes = element.GetAttributes() timeout$ = imageFileAttributes.Lookup("timeout") interactiveMenuItem.targetImageFileTimeout% = int(val(timeout$)) interactiveMenuItem.targetImageFileUseImageBuffer = ParseBoolAttribute(interactiveMenuItemXML.targetImageFile[0], "useImageBuffer") else if interactiveMenuItem.targetVideoFile$ <> "" then interactiveMenuItem.probeData = GetProbeData(bsp.assetPoolFiles, interactiveMenuItem.targetVideoFile$) else if interactiveMenuItem.targetAudioFile$ <> "" then interactiveMenuItem.probeData = GetProbeData(bsp.assetPoolFiles, interactiveMenuItem.targetAudioFile$) endif else if interactiveMenuItem.targetType$ = "mediaState" then interactiveMenuItem.targetMediaState$ = interactiveMenuItemXML.targetMediaState.GetText() endif nextIsPrevious$ = interactiveMenuItemXML.targetIsPreviousState.GetText() interactiveMenuItem.targetIsPreviousState = false if nextIsPrevious$ <> "" and lcase(nextIsPrevious$) = "yes" then interactiveMenuItem.targetIsPreviousState = true endif interactiveMenuItem.enterCmds = CreateObject("roArray", 1, true) cmdsXML = interactiveMenuItemXML.enterBrightSignCmds.brightSignCmd if cmdsXML.Count() > 0 then for each cmdXML in cmdsXML newCmd(bsp, cmdXML, interactiveMenuItem.enterCmds) next endif for each cmd in interactiveMenuItem.enterCmds commandName$ = cmd.name$ if commandName$ = "sendUDPCommand" or commandName$ = "synchronize" then bsp.CreateUDPSender(bsp) else if commandName$ = "sendSerialStringCommand" or commandName$ = "sendSerialBlockCommand" or commandName$ = "sendSerialByteCommand" or commandName$ = "sendSerialBytesCommand" then port$ = cmd.parameters["port"].GetCurrentParameterValue() bsp.CreateSerial(bsp, port$, true) endif next interactiveMenuItem.upNavigationIndex$ = interactiveMenuItemXML.upNavigationMenuItem.GetText() if IsString(interactiveMenuItem.upNavigationIndex$) and interactiveMenuItem.upNavigationIndex$ <> "" then interactiveMenuItem.upNavigationIndex% = int(val(interactiveMenuItem.upNavigationIndex$)) else interactiveMenuItem.upNavigationIndex% = -1 endif interactiveMenuItem.downNavigationIndex$ = interactiveMenuItemXML.downNavigationMenuItem.GetText() if IsString(interactiveMenuItem.downNavigationIndex$) and interactiveMenuItem.downNavigationIndex$ <> "" then interactiveMenuItem.downNavigationIndex% = int(val(interactiveMenuItem.downNavigationIndex$)) else interactiveMenuItem.downNavigationIndex% = -1 endif interactiveMenuItem.leftNavigationIndex$ = interactiveMenuItemXML.leftNavigationMenuItem.GetText() if IsString(interactiveMenuItem.leftNavigationIndex$) and interactiveMenuItem.leftNavigationIndex$ <> "" then interactiveMenuItem.leftNavigationIndex% = int(val(interactiveMenuItem.leftNavigationIndex$)) else interactiveMenuItem.leftNavigationIndex% = -1 endif interactiveMenuItem.rightNavigationIndex$ = interactiveMenuItemXML.rightNavigationMenuItem.GetText() if IsString(interactiveMenuItem.rightNavigationIndex$) and interactiveMenuItem.rightNavigationIndex$ <> "" then interactiveMenuItem.rightNavigationIndex% = int(val(interactiveMenuItem.rightNavigationIndex$)) else interactiveMenuItem.rightNavigationIndex% = -1 endif return interactiveMenuItem End Function Function ParseFileNameXML(fileNameXML As Object) As String if fileNameXML.Count() = 1 then fileElement = fileNameXML[0] fileAttributes = fileElement.GetAttributes() fileName$ = fileAttributes.Lookup("name") else fileName$ = "" endif return fileName$ End Function Function ParseBoolAttribute(elementXML As Object, attr$ As String) As Boolean element = elementXML attributes = element.GetAttributes() val = attributes.Lookup(attr$) if IsString(val) and lcase(val) = "true" then return true endif return false End Function Function ParseNavigation(bsp As Object, sign As Object, navigationXML As Object) As Object navigation = invalid if navigationXML.Count() = 1 then navigationElement = navigationXML[0] userEventsList = navigationElement.userEvent if userEventsList.Count() > 0 then navigation = CreateObject("roAssociativeArray") for each userEvent in userEventsList ParseUserEvent(bsp, sign, navigation, userEvent) next endif endif return navigation End Function Sub ParseUserEvent(bsp As Object, sign As Object, aa As Object, userEvent As Object) userEventName$ = userEvent.name.GetText() if userEventName$ = "keyboard" then aa.keyboardChar$ = userEvent.parameters.parameter.GetText() if len(aa.keyboardChar$) > 1 then aa.keyboardChar$ = Lcase(aa.keyboardChar$) endif else if userEventName$ = "zoneMessage" then aa.zoneMessage$ = userEvent.parameters.parameter.GetText() else if userEventName$ = "remote" then aa.remoteEvent$ = ucase(userEvent.parameters.parameter.GetText()) if type(bsp.remote) <> "roIRRemote" then bsp.remote = CreateObject("roIRRemote") bsp.remote.SetPort(bsp.msgPort) endif else if userEventName$ = "serial" then port$ = userEvent.parameters.parameter.GetText() serial$ = userEvent.parameters.parameter2.GetText() port% = int(val(port$)) serialPortConfiguration = sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ aa.serialEvent = CreateObject("roAssociativeArray") aa.serialEvent.port$ = port$ aa.serialEvent.protocol$ = protocol$ if protocol$ = "Binary" then aa.serialEvent.inputSpec = ConvertToByteArray(serial$) aa.serialEvent.asciiSpec = serial$ else aa.serialEvent.serial$ = serial$ endif bsp.CreateSerial(bsp, aa.serialEvent.port$, false) else if userEventName$ = "bp900AUserEvent" or userEventName$ = "bp900BUserEvent" or userEventName$ = "bp900CUserEvent" or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" then aa.bpEvent = {} aa.bpEvent.buttonPanelIndex% = int(val(userEvent.parameters.buttonPanelIndex.GetText())) aa.bpEvent.buttonNumber$ = userEvent.parameters.buttonNumber.GetText() bsp.ConfigureBPInput(aa.bpEvent.buttonPanelIndex%, aa.bpEvent.buttonNumber$) else if userEventName$ = "gpioUserEvent" then if userEvent.parameters.buttonNumber.GetText() <> "" then buttonNumber$ = userEvent.parameters.buttonNumber.GetText() else buttonNumber$ = userEvent.parameters.parameter.GetText() endif aa.gpioEvent = {} aa.gpioEvent.buttonNumber$ = buttonNumber$ bsp.ConfigureGPIOInput(buttonNumber$) endif End Sub Sub newBaseTemplateItem(templateItem As Object, templateItemXML As Object) templateItem.type$ = templateItemXML.GetName() templateItem.x% = int(val(templateItemXML.x.GetText())) templateItem.y% = int(val(templateItemXML.y.GetText())) templateItem.width% = int(val(templateItemXML.width.GetText())) templateItem.height% = int(val(templateItemXML.height.GetText())) templateItem.layer% = int(val(templateItemXML.layer.GetText())) End Sub Sub ParseTemplateWidgets(item As Object, itemXML As Object) ' text widget items item.numberOfLines% = int(val(itemXML.textWidget.numberOfLines.GetText())) item.rotation$ = "0" ele = itemXML.textWidget.GetNamedElements("rotation") if ele.Count() = 1 then rotation$ = ele[0].GetText() if rotation$ = "90" then item.rotation$ = "270" else if rotation$ = "180" then item.rotation$ = "180" else if rotation$ = "270" then item.rotation$ = "90" endif endif item.alignment$ = "left" ele = itemXML.textWidget.GetNamedElements("alignment") if ele.Count() = 1 then alignment$ = ele[0].GetText() if alignment$ = "center" then item.alignment$ = "center" else if alignment$ = "right" then item.alignment$ = "right" endif endif ' widget items item.foregroundTextColor$ = GetHexColor(itemXML.widget.foregroundTextColor.GetAttributes()) item.backgroundTextColor$ = GetHexColor(itemXML.widget.backgroundTextColor.GetAttributes()) item.font$ = itemXML.widget.font.GetText() if item.font$ = "" then item.font$ = "System" item.fontSize% = 0 if type(itemXML.widget.fontSize) = "roXMLList" and itemXML.widget.fontSize.Count() = 1 then if itemXML.widget.fontSize.GetText() <> "" then item.fontSize% = int(val(itemXML.widget.fontSize.GetText())) endif endif item.backgroundColorSpecified = false if lcase(itemXML.backgroundColorSpecified.GetText()) = "true" then item.backgroundColorSpecified = true endif End Sub Sub newTextTemplateItem(templateItem As Object, templateItemXML As Object) ' fill in base class members newBaseTemplateItem(templateItem, templateItemXML) ParseTemplateWidgets(templateItem, templateItemXML) End Sub Sub newConstantTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) templateItem.textString$ = templateItemXML.text.GetText() templateItems.push(templateItem) End Sub Sub newSystemVariableTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) templateItem.systemVariableType$ = templateItemXML.systemVariable.GetText() templateItems.push(templateItem) End Sub Sub newMediaCounterTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) fileName$ = templateItemXML.fileName.GetText() templateItem.userVariable = bsp.GetUserVariable(fileName$) if type(templateItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("Media counter variable " + fileName$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_MEDIA_COUNTER_VARIABLE_NOT_FOUND, fileName$) endif templateItems.push(templateItem) End Sub Sub newUserVariableTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) name$ = templateItemXML.name.GetText() templateItem.userVariable = bsp.GetUserVariable(name$) if type(templateItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("User variable " + name$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, name$) endif templateItems.push(templateItem) End Sub Function newLiveTextDataEntryTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) liveDataFeedName$ = CleanName(templateItemXML.liveDataFeedName.GetText()) templateItem.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) return templateItem End Function Sub newIndexedLiveTextDataEntryTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = newLiveTextDataEntryTemplateItem(bsp, templateItems, templateItemXML) index$ = templateItemXML.index.GetText() if index$ <> "" then ' old style index was zero based; new style is 1 based. index% = int(val(index$)) index% = index% + 1 index$ = StripLeadingSpaces(str(index%)) templateItem.index = newTextParameterValue(index$) else templateItem.index = newParameterValue(bsp, templateItemXML.indexSpec.parameterValue) endif templateItems.push(templateItem) End Sub Sub newTitledLiveTextDataEntryTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = newLiveTextDataEntryTemplateItem(bsp, templateItems, templateItemXML) title$ = templateItemXML.title.GetText() if title$ <> "" then templateItem.title = newTextParameterValue(title$) else templateItem.title = newParameterValue(bsp, templateItemXML.titleSpec.parameterValue) endif templateItems.push(templateItem) End Sub Function newSimpleRSSTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newTextTemplateItem(templateItem, templateItemXML) templateItem.id$ = templateItemXML.id.GetText() templateItem.elementName$ = templateItemXML.elementName.GetText() templateItems.push(templateItem) return templateItem End Function Sub newImageTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) templateItem = { } newBaseTemplateItem(templateItem, templateItemXML) templateItem.fileName$ = templateItemXML.fileName.GetText() templateItems.push(templateItem) End Sub Function newMRSSTextTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) As Object templateItem = { } newTextTemplateItem(templateItem, templateItemXML) templateItems.push(templateItem) return templateItem End Function Function newMRSSImageTemplateItem(bsp As Object, templateItems As Object, templateItemXML As Object) As Object templateItem = { } newBaseTemplateItem(templateItem, templateItemXML) templateItems.push(templateItem) return templateItem End Function Sub newTemplateItem(bsp As Object, state As Object, templateItems As Object, templateItemXML As Object) name$ = templateItemXML.GetName() if name$ = "constantTextTemplateItem" then newConstantTextTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "systemVariableTextTemplateItem" then newSystemVariableTextTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "mediaCounterTemplateItem" then newMediaCounterTextTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "userVariableTemplateItem" then newUserVariableTextTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "indexedLiveTextDataEntryTemplateItem" then newIndexedLiveTextDataEntryTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "titledLiveTextDataEntryTemplateItem" then newTitledLiveTextDataEntryTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "imageTemplateItem" then newImageTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "simpleRSSTextTemplateItem" then templateItem = newSimpleRSSTextTemplateItem(bsp, templateItems, templateItemXML) if type(state.simpleRSSTextTemplateItems) <> "roAssociativeArray" then state.simpleRSSTextTemplateItems = { } endif simpleRSS = state.simpleRSSTextTemplateItems.Lookup(templateItem.id$) if type(simpleRSS) <> "roAssociativeArray" then simpleRSS = { } simpleRSS.currentIndex% = 0 simpleRSS.liveDataFeed = templateItem.liveDataFeed simpleRSS.displayTime% = int(val(templateItemXML.displayTime.GetText())) rssLiveDataFeedNamesXMLList = templateItemXML.GetNamedElements("rssLiveDataFeedName") if type(rssLiveDataFeedNamesXMLList) = "roXMLList" and rssLiveDataFeedNamesXMLList.Count() > 0 then simpleRSS.rssLiveDataFeeds = CreateObject("roArray", rssLiveDataFeedNamesXMLList.Count(), true) for each rssLiveDataFeedNameXML in rssLiveDataFeedNamesXMLList liveDataFeedName$ = CleanName(rssLiveDataFeedNameXML.GetText()) liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) if type(liveDataFeed) = "roAssociativeArray" then simpleRSS.rssLiveDataFeeds.push(liveDataFeed) endif next endif simpleRSS.currentLiveDataFeedIndex% = 0 simpleRSS.items = CreateObject("roArray", 1, true) state.simpleRSSTextTemplateItems.AddReplace(templateItem.id$, simpleRSS) endif simpleRSS.items.push(templateItem) else if name$ = "mrssTextTemplateItem" then elementName$ = templateItemXML.elementName.GetText() templateItem = newMRSSTextTemplateItem(bsp, templateItems, templateItemXML) if elementName$ = "title" then state.mrssTitleTemplateItem = templateItem else if elementName$ = "description" then state.mrssDescriptionTemplateItem = templateItem endif else if name$ = "mrssImageTemplateItem" then state.mrssImageTemplateItem = newMRSSImageTemplateItem(bsp, templateItems, templateItemXML) endif End Sub Sub ParseTemplateBackgroundImageXML(bsp As Object, state As Object, playlistItemXML As Object) backgroundImageXML = playlistItemXML.backgroundImage if backgroundImageXML.Count() = 1 then backgroundImageFileElement = backgroundImageXML[0] backgroundImageAttributes = backgroundImageFileElement.GetAttributes() state.backgroundImage$ = backgroundImageAttributes.Lookup("name") if backgroundImageFileElement.HasAttribute("width") then state.backgroundImageWidth% = int(val(backgroundImageAttributes.Lookup("width"))) else state.backgroundImageWidth% = -1 endif if backgroundImageFileElement.HasAttribute("height") then state.backgroundImageHeight% = int(val(backgroundImageAttributes.Lookup("height"))) else state.backgroundImageHeight% = -1 endif else state.backgroundImage$ = "" endif state.backgroundImageUserVariable = bsp.GetUserVariable(state.backgroundImage$) End Sub Sub SetTemplateHandlers(state As Object) state.HStateEventHandler = STTemplatePlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.PreloadItem = PreloadItem state.SetBackgroundImageSizeLocation = SetBackgroundImageSizeLocation state.ScaleBackgroundImageToFit = ScaleBackgroundImageToFit state.SetupTemplateMRSS = SetupTemplateMRSS state.FindMRSSContent = FindMRSSContent state.GetNextMRSSTemplateItem = GetNextMRSSTemplateItem state.GetMRSSTemplateItem = GetMRSSTemplateItem state.RedisplayTemplateItems = RedisplayTemplateItems state.BuildTemplateItems = BuildTemplateItems state.BuildTemplateItem = BuildTemplateItem state.BuildTextTemplateItem = BuildTextTemplateItem state.TemplateUsesAnyUserVariable = TemplateUsesAnyUserVariable state.TemplateUsesUserVariable = TemplateUsesUserVariable state.TemplateUsesSystemVariable = TemplateUsesSystemVariable state.LaunchWaitForContentTimer = LaunchWaitForContentTimer End Sub Sub newTemplatePlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) ParseTemplateBackgroundImageXML(bsp, state, playlistItemXML) ' retrieve mrss live data feed names mrssLiveDataFeedNamesXMLList = playlistItemXML.GetNamedElements("mrssLiveDataFeedName") if type(mrssLiveDataFeedNamesXMLList) = "roXMLList" and mrssLiveDataFeedNamesXMLList.Count() > 0 then state.mrssActive = true state.mrssLiveDataFeeds = CreateObject("roArray", mrssLiveDataFeedNamesXMLList.Count(), true) for each mrssLiveDataFeedNameXML in mrssLiveDataFeedNamesXMLList liveDataFeedName$ = CleanName(mrssLiveDataFeedNameXML.GetText()) liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) if type(liveDataFeed) = "roAssociativeArray" then state.mrssLiveDataFeeds.push(liveDataFeed) endif next else state.mrssActive = false endif ' retrieve template items state.templateItems = CreateObject("roArray", 1, true) childElements = playlistItemXML.GetChildElements() for each childElement in childElements newTemplateItem(bsp, state, state.templateItems, childElement) next SetTemplateHandlers(state) End Sub Sub newTemplatePlaylistItemFromLiveTextPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) ParseTemplateBackgroundImageXML(bsp, state, playlistItemXML) state.mrssActive = false ' compatibility with old data format liveDataFeedUpdateInterval% = 600 if playlistItemXML.liveTextRSSUpdateInterval.GetText() <> "" then liveDataFeedUpdateInterval% = int(val(playlistItemXML.liveTextRSSUpdateInterval.GetText())) endif textItemsXML = playlistItemXML.textItem textItems = CreateObject("roArray", 1, true) for each textItemXML in textItemsXML newTextItem(bsp, textItems, textItemXML, liveDataFeedUpdateInterval%) next state.templateItems = CreateObject("roArray", 1, true) for each textItem in textItems templateItem = { } newTextTemplateItemFromTextItem(templateItem, textItem) if textItem.textType$ = "constant" then templateItem.type$ = "constantTextTemplateItem" templateItem.textString$ = textItem.textString$ else if textItem.textType$ = "system" then templateItem.type$ = "systemVariableTextTemplateItem" templateItem.systemVariableType$ = textItem.systemVariableType$ else if textItem.textType$ = "indexedLiveTextData" then templateItem.type$ = "indexedLiveTextDataEntryTemplateItem" templateItem.liveDataFeed = textItem.liveDataFeed ' old style index was zero based; new style is 1 based. index% = textItem.index% index% = index% + 1 index$ = StripLeadingSpaces(str(index%)) templateItem.index = newTextParameterValue(index$) else if textItem.textType$ = "titledLiveTextData" then templateItem.type$ = "titledLiveTextDataEntryTemplateItem" templateItem.liveDataFeed = textItem.liveDataFeed templateItem.title = newTextParameterValue(textItem.title$) else if textItem.textType$ = "mediaCounter" then templateItem.type$ = "mediaCounterTemplateItem" templateItem.userVariable = textItem.userVariable else if textItem.textType$ = "userVariable" then templateItem.type$ = "userVariableTemplateItem" templateItem.userVariable = textItem.userVariable endif state.templateItems.push(templateItem) next SetTemplateHandlers(state) End Sub Sub newBaseTemplateItemFromTextItem(templateItem As Object, textItem As Object) templateItem.x% = textItem.x% templateItem.y% = textItem.y% templateItem.width% = textItem.width% templateItem.height% = textItem.height% templateItem.layer% = 1 End Sub Sub newTextTemplateItemFromTextItem(templateItem As Object, textItem As Object) ' fill in base class members newBaseTemplateItemFromTextItem(templateItem, textItem) ' text widget items templateItem.numberOfLines% = textItem.numberOfLines% templateItem.rotation$ = textItem.rotation$ templateItem.alignment$ = textItem.alignment$ ' widget items templateItem.foregroundTextColor$ = textItem.foregroundTextColor$ templateItem.backgroundTextColor$ = textItem.backgroundTextColor$ templateItem.font$ = textItem.font$ templateItem.fontSize% = textItem.fontSize% templateItem.backgroundColorSpecified = textItem.backgroundColorSpecified End Sub Sub newTextItem(bsp As Object, textItems As Object, textItemXML As Object, liveDataFeedUpdateInterval% As Integer) textItem = CreateObject("roAssociativeArray") textItem.x% = int(val(textItemXML.x.GetText())) textItem.y% = int(val(textItemXML.y.GetText())) textItem.width% = int(val(textItemXML.width.GetText())) textItem.height% = int(val(textItemXML.height.GetText())) if textItemXML.textItemConstant.Count() = 1 then textItem.textType$ = "constant" textItemConstantElement = textItemXML.textItemConstant[0] textItem.textString$ = textItemConstantElement.text.GetText() else if textItemXML.textItemSystemVariable.Count() = 1 then textItem.textType$ = "system" textItemSystemVariableElement = textItemXML.textItemSystemVariable[0] textItem.systemVariableType$ = textItemSystemVariableElement.systemVariable.GetText() else if textItemXML.textItemIndexedLiveTextDataEntry.Count() = 1 then textItem.textType$ = "indexedLiveTextData" textItemIndexedLiveTextDataEntryElement = textItemXML.textItemIndexedLiveTextDataEntry[0] index$ = textItemIndexedLiveTextDataEntryElement.Index.GetText() textItem.index% = int(val(index$)) if textItemIndexedLiveTextDataEntryElement.liveDataFeedName.GetText() <> "" then liveDataFeedName$ = CleanName(textItemIndexedLiveTextDataEntryElement.liveDataFeedName.GetText()) textItem.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) else if type(textItemIndexedLiveTextDataEntryElement.LiveTextDataUrlSpec) = "roXMLList" and textItemIndexedLiveTextDataEntryElement.LiveTextDataUrlSpec.Count() = 1 then url = newParameterValue(bsp, textItemIndexedLiveTextDataEntryElement.LiveTextDataUrlSpec.parameterValue) else liveTextDataUrl$ = textItemIndexedLiveTextDataEntryElement.LiveTextDataUrl.GetText() url = newTextParameterValue(liveTextDataUrl$) endif liveDataFeed = newLiveDataFeedFromOldDataFormat(bsp, CleanName(url), liveDataFeedUpdateInterval%) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif else if textItemXML.textItemTitledLiveTextDataEntry.Count() = 1 then textItem.textType$ = "titledLiveTextData" textItemTitledLiveTextDataEntryElement = textItemXML.textItemTitledLiveTextDataEntry[0] textItem.title$ = textItemTitledLiveTextDataEntryElement.Title.GetText() if textItemTitledLiveTextDataEntryElement.liveDataFeedName.GetText() <> "" then liveDataFeedName$ = CleanName(textItemTitledLiveTextDataEntryElement.liveDataFeedName.GetText()) textItem.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) else if type(textItemTitledLiveTextDataEntryElement.LiveTextDataUrlSpec) = "roXMLList" and textItemTitledLiveTextDataEntryElement.LiveTextDataUrlSpec.Count() = 1 then url = newParameterValue(bsp, textItemTitledLiveTextDataEntryElement.LiveTextDataUrlSpec.parameterValue) else liveTextDataUrl$ = textItemTitledLiveTextDataEntryElement.LiveTextDataUrl.GetText() url = newTextParameterValue(liveTextDataUrl$) endif liveDataFeed = newLiveDataFeedFromOldDataFormat(bsp, CleanName(url), liveDataFeedUpdateInterval%) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif else if textItemXML.textItemMediaCounter.Count() = 1 then textItem.textType$ = "mediaCounter" fileName$ = textItemXML.textItemMediaCounter.fileName.GetText() textItem.userVariable = bsp.GetUserVariable(fileName$) if type(textItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("Media counter variable " + fileName$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_MEDIA_COUNTER_VARIABLE_NOT_FOUND, fileName$) endif else if textItemXML.textItemUserVariable.Count() = 1 then textItem.textType$ = "userVariable" userVariableName$ = textItemXML.textItemUserVariable.name.GetText() textItem.userVariable = bsp.GetUserVariable(userVariableName$) if type(textItem.userVariable) <> "roAssociativeArray" then bsp.diagnostics.PrintDebug("User variable " + userVariableName$ + " not found.") bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, fileName$) endif else stop endif ParseTemplateWidgets(textItem, textItemXML) textItems.push(textItem) End Sub Sub newAudioInPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) file = playlistItemXML.file fileAttrs = file.GetAttributes() state.imageFileName$ = fileAttrs["name"] state.imageUserVariable = bsp.GetUserVariable(state.imageFileName$) state.useImageBuffer = false useImageBuffer$ = playlistItemXML.useImageBuffer.GetText() if len(useImageBuffer$) > 0 then useImageBuffer$ = lcase(useImageBuffer$) if useImageBuffer$ = "true" then state.useImageBuffer = true endif endif state.HStateEventHandler = STAudioInPlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Sub newSignChannelPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.slideTransition% = 15 signChannelRevision$ = "315" url$ = "http://rss.signchannel.com/productId=RK" + bsp.sysInfo.deviceModel$ + "/frameId=" + bsp.sysInfo.deviceUniqueID$ + "/version=" + signChannelRevision$ liveDataFeedName$ = "signChannel" liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) if type(liveDataFeed) <> "roAssociativeArray" then liveDataFeed = newLiveDataFeedFromMRSSFormat( bsp, liveDataFeedName$, url$, false ) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif state.liveDataFeed = liveDataFeed SetMRSSHandlers( state ) End Sub Sub newMRSSPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.slideTransition% = 0 liveDataFeedName$ = CleanName( playlistItemXML.liveDataFeedName.GetText() ) if liveDataFeedName$ <> "" then state.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) endif SetMRSSHandlers( state ) End Sub Sub newRSSImagePlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.slideTransition% = 0 rssSpec = playlistItemXML.rssSpec rssSpecAttrs = rssSpec.GetAttributes() isDynamicPlaylist = false if rssSpecAttrs.DoesExist("usesBSNDynamicPlaylist") then usesDynamicPlaylist = rssSpecAttrs.Lookup("usesBSNDynamicPlaylist") if lcase(usesDynamicPlaylist) = "true" then isDynamicPlaylist = true endif endif if rssSpecAttrs.DoesExist("url") then url$ = rssSpecAttrs["url"] else url = newParameterValue(bsp, playlistItemXML.urlSpec.parameterValue) url$ = url.GetCurrentParameterValue() endif liveDataFeedName$ = CleanName( url$ ) liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) if type(liveDataFeed) <> "roAssociativeArray" then liveDataFeed = newLiveDataFeedFromMRSSFormat( bsp, liveDataFeedName$, url$, isDynamicPlaylist ) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif state.liveDataFeed = liveDataFeed SetMRSSHandlers( state ) End Sub Sub newLocalPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.slideTransition% = 0 attrs = playlistItemXML.defaultPlaylist.mediaRSSSpec.GetAttributes() if attrs = invalid then ' new format liveDataFeedName$ = playlistItemXML.defaultPlaylistDataFeedName.GetText() devicePlaylists = playlistItemXML.devicePlaylist for each devicePlaylist in devicePlaylists deviceName$ = devicePlaylist.deviceName.GetText() deviceLiveDataFeedName$ = devicePlaylist.dynamicPlaylistDataFeedName.GetText() if deviceName$ = bsp.sysInfo.deviceUniqueID$ then liveDataFeedName$ = CleanName(deviceLiveDataFeedName$) exit for endif next if liveDataFeedName$ <> "" then state.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) endif else url$ = attrs["url"] name$ = attrs["name"] devicePlaylists = playlistItemXML.devicePlaylist for each devicePlaylist in devicePlaylists deviceName$ = devicePlaylist.deviceName.GetText() attrs = devicePlaylist.dynamicPlaylist.mediaRSSSpec.GetAttributes() dynamicPlaylistUrl$ = attrs["url"] if deviceName$ = bsp.sysInfo.deviceUniqueID$ then url$ = dynamicPlaylistUrl$ name$ = attrs["name"] exit for endif next liveDataFeedName$ = CleanName(name$) liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) if type(liveDataFeed) <> "roAssociativeArray" then liveDataFeed = newLiveDataFeedFromMRSSFormat( bsp, liveDataFeedName$, url$, true ) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif state.liveDataFeed = liveDataFeed endif SetMRSSHandlers( state ) End Sub Sub SetMRSSHandlers( state As Object ) state.HStateEventHandler = STPlayingMediaRSSEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.PreloadItem = PreloadItem state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchWaitForContentTimer = LaunchWaitForContentTimer state.ProtectMRSSFeed = ProtectMRSSFeed state.ProtectMRSSItem = ProtectMRSSItem state.DisplayMRSSItem = DisplayMRSSItem state.AdvanceToNextMRSSItem = AdvanceToNextMRSSItem state.AtEndOfFeed = AtEndOfFeed state.DisplayImage = DisplayImage state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.LaunchVideo = LaunchVideo state.PrePlayVideo = PrePlayVideo state.PlayVideo = PlayVideo state.PostPlayVideo = PostPlayVideo state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents End Sub Sub newTripleUSBPlaylistItem(playlistItemXML As Object, sign As Object, state As Object) state.boseProductName$ = playlistItemXML.boseProductName.GetText() state.noiseThreshold% = int(val(playlistItemXML.noiseThreshold.GetText())) state.tripleUSBPort$ = sign.tripleUSBPort$ boseProductSpec = state.bsp.GetBoseProductSpec(state.boseProductName$) if type(boseProductSpec) = "roAssociativeArray" then boseProductInSign = sign.boseProducts.Lookup(state.boseProductName$) state.boseProductPort$ = boseProductInSign.port$ state.volumeTable = boseProductSpec.volumeTable else state.boseProductPort$ = "" state.volumeTable = invalid endif state.HStateEventHandler = STTripleUSBEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.SendVolumeCommand = SendVolumeCommand End Sub Function GetBoolFromXML(xmlValue$ As String) As Boolean value$ = lcase(xmlValue$) if value$ = "true" then return true else return false endif End Function Sub SetStateEvent(bsp As Object, stateEvent as Object, eventXML As Object) userEventsList = eventXML.userEvent if userEventsList.Count() = 0 then print "Invalid XML file - userEvent not found" : stop for each userEvent in userEventsList userEventName$ = userEvent.name.GetText() if userEventName$ = "gpioUserEvent" then if userEvent.parameters.buttonNumber.GetText() <> "" then stateEvent.gpioUserEventButtonNumber$ = userEvent.parameters.buttonNumber.GetText() else stateEvent.gpioUserEventButtonNumber$ = userEvent.parameters.parameter.GetText() endif bsp.ConfigureGPIOInput(stateEvent.gpioUserEventButtonNumber$) else if userEventName$ = "bp900AUserEvent" or userEventName$ = "bp900BUserEvent" or userEventName$ = "bp900CUserEvent" or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" then stateEvent.bpUserEventButtonNumber$ = userEvent.parameters.buttonNumber.GetText() stateEvent.bpUserEventButtonPanelIndex$ = userEvent.parameters.buttonPanelIndex.GetText() bpIndex% = int(val(stateEvent.bpUserEventButtonPanelIndex$)) bsp.ConfigureBPInput(bpIndex%, stateEvent.bpUserEventButtonNumber$) else if userEventName$ = "serial" then stateEvent.serialUserEventPort$ = userEvent.parameters.parameter.GetText() stateEvent.serialUserEventSerialEvent$ = userEvent.parameters.parameter2.GetText() bsp.CreateSerial(bsp, stateEvent.serialUserEventPort$, false) else if userEventName$ = "udp" then stateEvent.udpUserEvent$ = userEvent.parameters.parameter.GetText() stateEvent.udpLabel$ = userEvent.parameters.label.GetText() if stateEvent.udpLabel$ = "" then stateEvent.udpLabel$ = stateEvent.udpUserEvent$ endif export$ = LCase(userEvent.parameters.export.GetText()) if export$ = "true" then stateEvent.udpExport = true else stateEvent.udpExport = false endif else if userEventName$ = "keyboard" then stateEvent.keyboardUserEvent$ = userEvent.parameters.parameter.GetText() else if userEventName$ = "zoneMessage" then stateEvent.zoneMessageUserEvent$ = userEvent.parameters.parameter.GetText() else if userEventName$ = "remote" then stateEvent.remoteUserEvent$ = ucase(userEvent.parameters.parameter.GetText()) if type(bsp.remote) <> "roIRRemote" then bsp.remote = CreateObject("roIRRemote") bsp.remote.SetPort(bsp.msgPort) endif endif next End Sub Sub newMediaListPlaylistItem(bsp As Object, zoneHSM As Object, playlistItemXML As Object, state As Object) if zoneHSM.type$ <> "EnhancedAudio" then if type(bsp.mediaListInactivity) <> "roAssociativeArray" then bsp.mediaListInactivity = CreateObject("roAssociativeArray") bsp.mediaListInactivity.mediaListStates = CreateObject("roList") endif bsp.mediaListInactivity.mediaListStates.AddTail(state) endif state.mediaType$ = playlistItemXML.mediaType.GetText() state.advanceOnMediaEnd = GetBoolFromXML(playlistItemXML.advanceOnMediaEnd.GetText()) state.advanceOnImageTimeout = GetBoolFromXML(playlistItemXML.advanceOnImageTimeout.GetText()) state.playFromBeginning = GetBoolFromXML(playlistItemXML.playFromBeginning.GetText()) state.shuffle = GetBoolFromXML(playlistItemXML.shuffle.GetText()) state.populateFromMediaLibrary = true populateFromMediaLibrary = playlistItemXML.populateFromMediaLibrary.GetText() if lcase(populateFromMediaLibrary) = "false" then state.populateFromMediaLibrary = false endif liveDataFeedName$ = playlistItemXML.liveDataFeedName.GetText() if liveDataFeedName$ <> "" then state.liveDataFeed = bsp.liveDataFeeds.Lookup(CleanName(liveDataFeedName$)) else state.liveDataFeed = invalid endif imageTimeout$ = lcase(playlistItemXML.imageTimeout.GetText()) if imageTimeout$ = "" then state.imageTimeout = 5000 else state.imageTimeout = val(imageTimeout$) * 1000 endif if type(playlistItemXML.next) = "roXMLList" and playlistItemXML.next.Count() = 1 then state.nextNavigation = CreateObject("roAssociativeArray") SetStateEvent(bsp, state.nextNavigation, playlistItemXML.next) endif if type(playlistItemXML.previous) = "roXMLList" and playlistItemXML.previous.Count() = 1 then state.previousNavigation = CreateObject("roAssociativeArray") SetStateEvent(bsp, state.previousNavigation, playlistItemXML.previous) endif if state.mediaType$ = "image" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif if state.populateFromMediaLibrary then if state.mediaType$ = "image" then imageItemList = playlistItemXML.files.imageItem if type(imageItemList) <> "roXMLList" then print "Invalid XML file - item list not found" : stop state.numItems% = imageItemList.Count() state.items = CreateObject("roArray", state.numItems%, true) for each imageItem in imageItemList item = CreateObject("roAssociativeArray") newImagePlaylistItem(bsp, imageItem, state, item) state.items.push(item) next else if state.mediaType$ = "video" then videoItemList = playlistItemXML.files.videoItem if type(videoItemList) <> "roXMLList" then print "Invalid XML file - item list not found" : stop state.numItems% = videoItemList.Count() state.items = CreateObject("roArray", state.numItems%, true) for each videoItem in videoItemList item = CreateObject("roAssociativeArray") newVideoPlaylistItem(bsp, videoItem, state, item) state.items.push(item) next else if state.mediaType$ = "audio" then audioItemList = playlistItemXML.files.audioItem if type(audioItemList) <> "roXMLList" then print "Invalid XML file - item list not found" : stop state.numItems% = audioItemList.Count() state.items = CreateObject("roArray", state.numItems%, true) for each audioItem in audioItemList item = CreateObject("roAssociativeArray") newAudioPlaylistItem(bsp, audioItem, state, item) state.items.push(item) next endif state.playbackIndices = CreateObject("roArray", state.numItems%, true) for i% = 0 to state.numItems%-1 state.playbackIndices[i%] = i% next else state.numItems% = 0 endif state.playbackActive = false state.playbackIndex% = 0 state.HStateEventHandler = STDisplayingMediaListItemEventHandler state.PopulateMediaListFromLiveDataFeed = PopulateMediaListFromLiveDataFeed state.ShuffleMediaListContent = ShuffleMediaListContent state.ConfigureIntraStateEventHandlerButton = ConfigureIntraStateEventHandlerButton state.PrePlayAudio = PrePlayAudio state.PlayAudio = PlayAudio state.PostPlayAudio = PostPlayAudio state.PlayMixerAudio = PlayMixerAudio state.PrePlayVideo = PrePlayVideo state.PlayVideo = PlayVideo state.PostPlayVideo = PostPlayVideo state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.StartInactivityTimer = StartInactivityTimer state.HandleIntraStateEvent = HandleIntraStateEvent state.LaunchMediaListPlaybackItem = LaunchMediaListPlaybackItem state.AdvanceMediaListPlayback = AdvanceMediaListPlayback state.RetreatMediaListPlayback = RetreatMediaListPlayback state.AddVideoTimeCodeEvent = AddVideoTimeCodeEvent state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.PreloadItem = PreloadItem state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Sub newPlayFilePlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) state.filesTable = CreateObject("roAssociativeArray") state.mediaType$ = playlistItemXML.mediaType.GetText() state.slideTransition% = GetSlideTransitionValue(playlistItemXML.slideTransition.GetText()) state.payload$ = "" state.specifyLocalFiles = GetBoolFromXML(playlistItemXML.specifyLocalFiles.GetText()) liveDataFeedName$ = playlistItemXML.liveDataFeedName.GetText() if liveDataFeedName$ <> "" then state.liveDataFeed = bsp.liveDataFeeds.Lookup(CleanName(liveDataFeedName$)) else state.liveDataFeed = invalid endif if state.specifyLocalFiles then files = playlistItemXML.filesTable.file if playlistItemXML.filesTable.file.Count() > 0 then for each file in files fileAttrs = file.GetAttributes() key$ = fileAttrs["key"] if fileAttrs.DoesExist("label") then label$ = fileAttrs["label"] else label$ = key$ endif if fileAttrs.DoesExist("export") and LCase(fileAttrs["export"]) <> "true" then export = false else export = true endif fileTableEntry = CreateObject("roAssociativeArray") fileTableEntry.label$ = label$ fileTableEntry.export = export fileTableEntry.fileName$ = fileAttrs["name"] fileTableEntry.fileType$ = fileAttrs["type"] if fileTableEntry.fileType$ = "video" or fileTableEntry.fileType$ = "audio" then fileTableEntry.probeData = GetProbeData(bsp.assetPoolFiles, fileTableEntry.fileName$) endif fileTableEntry.userVariable = bsp.GetUserVariable(fileTableEntry.fileName$) fileTableEntry.videoDisplayMode% = 0 videoDisplayMode$ = fileAttrs["videoDisplayMode"] if videoDisplayMode$ = "3DSBS" then fileTableEntry.videoDisplayMode% = 1 else if videoDisplayMode$ = "3DTOB" then fileTableEntry.videoDisplayMode% = 2 endif state.filesTable.AddReplace(key$, fileTableEntry) next endif endif state.HStateEventHandler = STPlayFileEventHandler state.PopulatePlayFileFromLiveDataFeed = PopulatePlayFileFromLiveDataFeed state.MediaItemEventHandler = MediaItemEventHandler state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames state.LaunchTimer = LaunchTimer state.DisplayImage = DisplayImage state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchVideo = LaunchVideo state.PrePlayVideo = PrePlayVideo state.PlayVideo = PlayVideo state.PostPlayVideo = PostPlayVideo state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents state.LaunchAudio = LaunchAudio state.PrePlayAudio = PrePlayAudio state.PlayAudio = PlayAudio state.PostPlayAudio = PostPlayAudio state.PreloadItem = PreloadItem End Sub Sub newStreamPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) streamSpecAttrs = playlistItemXML.streamSpec.GetAttributes() if type(streamSpecAttrs["url"]) = "roString" then state.url = newTextParameterValue(streamSpecAttrs["url"]) else state.url = newParameterValue(bsp, playlistItemXML.url.parameterValue) endif state.HStateEventHandler = STStreamPlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchTimer = LaunchTimer state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames End Sub Sub newMjpegStreamPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object) mjpegSpecAttrs = playlistItemXML.mjpegSpec.GetAttributes() if type(mjpegSpecAttrs["url"]) = "roString" then state.url = newTextParameterValue(mjpegSpecAttrs["url"]) else state.url = newParameterValue(bsp, playlistItemXML.url.parameterValue) endif state.rotation% = int(val(mjpegSpecAttrs["rotation"])) state.HStateEventHandler = STMjpegPlayingEventHandler state.MediaItemEventHandler = MediaItemEventHandler state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons state.LaunchTimer = LaunchTimer state.ExecuteTransition = ExecuteTransition state.GetNextStateName = GetNextStateName state.UpdatePreviousCurrentStateNames = UpdatePreviousCurrentStateNames End Sub Sub newAudioPlaylistItem(bsp As Object, playlistItemXML As Object, state As Object, playlistItemBS As Object) newMediaPlaylistItem(bsp, playlistItemXML, state, playlistItemBS) playlistItemBS.probeData = GetProbeData(bsp.assetPoolFiles, playlistItemBS.fileName$) itemVolume$ = playlistItemXML.volume.GetText() if itemVolume$ <> "" then playlistItemBS.volume% = int(val(itemVolume$)) endif state.HStateEventHandler = STAudioPlayingEventHandler state.LaunchAudio = LaunchAudio state.PrePlayAudio = PrePlayAudio state.PlayAudio = PlayAudio state.PostPlayAudio = PostPlayAudio state.LaunchMixerAudio = LaunchMixerAudio state.PlayMixerAudio = PlayMixerAudio state.ConfigureBPButtons = ConfigureBPButtons state.ConfigureGPIOButtons = ConfigureGPIOButtons End Sub Function newTextPlaylistItem(playlistItemXML As Object) As Object item = CreateObject("roAssociativeArray") strings = playlistItemXML.strings numTextStrings% = 0 if strings <> invalid then children = strings.GetChildElements() if children <> invalid then numTextStrings% = children.Count() end if end if item.textStrings = CreateObject("roArray", numTextStrings%, true) for each textStringXML in strings.GetChildElements() textString = textStringXML.GetText() item.textStrings.push(textString) next item.isRSSFeed = false return item End Function Function newTwitterPlaylistItem(bsp As Object, zoneHSM As Object, playlistItemXML As Object) As Object item = CreateObject("roAssociativeArray") ' read twitter user name twitterSpec = playlistItemXML.twitterSpec twitterSpecAttrs = twitterSpec.GetAttributes() if twitterSpecAttrs["AuthToken"] = invalid return invalid twitterUserName$ = twitterSpecAttrs["userName"] if Left(twitterUserName$, 1) = "@" then twitterUserName$ = Mid(twitterUserName$, 2) endif jsonUrl$ = "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + twitterUserName$ url = newTextParameterValue(jsonUrl$) authData = CreateObject("roAssociativeArray") authData.AuthType = "OAuth 1.0a" authData.AuthToken = twitterSpecAttrs["AuthToken"] authData.ConsumerKey = twitterSpecAttrs["BSConsumerKey"] authData.EncryptedTwitterSecrets = twitterSpecAttrs["EncryptedTwitterSecrets"] if type(twitterSpecAttrs["updateInterval"]) = "roString" then updateInterval$ = twitterSpecAttrs["updateInterval"] if updateInterval$ = "" then updateInterval$ = "300" endif updateInterval% = int(val(updateInterval$)) else updateInterval% = 300 endif liveDataFeed = newLiveDataFeedWithAuthData(bsp, url, authData, updateInterval%) if not bsp.liveDataFeeds.DoesExist(liveDataFeed.name$) then liveDataFeed.isJSON = true if type(twitterSpecAttrs["restrictNumberOfTweets"]) = "roString" then restrictNumberOfTweets$ = twitterSpecAttrs["restrictNumberOfTweets"] if lcase(restrictNumberOfTweets$) = "true" then if type(twitterSpecAttrs["numberOfTweetsToShow"]) = "roString" then numberOfTweetsToShow$ = twitterSpecAttrs["numberOfTweetsToShow"] if numberOfTweetsToShow$ <> "" then liveDataFeed.restrictNumberOfItems = true liveDataFeed.numberOfItemsToDisplay% = int(val(numberOfTweetsToShow$)) endif endif endif endif bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) else liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeed.name$) endif item.liveDataFeed = liveDataFeed item.rssTitle$ = item.liveDataFeed.name$ item.twitterUserName$ = twitterSpecAttrs["userName"] + ": " item.isRSSFeed = true return item End Function Function newRSSDataFeedPlaylistItem(bsp As Object, playlistItemXML As Object) As Object item = CreateObject("roAssociativeArray") liveDataFeedName$ = CleanName( playlistItemXML.liveDataFeedName.GetText() ) item.liveDataFeed = bsp.liveDataFeeds.Lookup(liveDataFeedName$) item.rssTitle$ = item.liveDataFeed.name$ item.isRSSFeed = true return item End Function Function newRSSPlaylistItem(bsp As Object, zoneHSM As Object, playlistItemXML As Object) As Object item = CreateObject("roAssociativeArray") rssSpec = playlistItemXML.rssSpec rssSpecAttrs = rssSpec.GetAttributes() url = newTextParameterValue(rssSpecAttrs["url"]) url$ = url.GetCurrentParameterValue() ' determine if this is a twitter feed isTwitterFeed = false index% = Instr(1, url$, "api.twitter.com") if index% > 0 then userNameIndex% = Instr(1, url$, "screen_name=") if userNameIndex% > 0 then isTwitterFeed = true item.twitterUserName$ = Mid(url$, userNameIndex% + 12) ' Be careful if you change the Twitter URL. It must be a normalized form for OAuth ' authentication to work. (Refer to OAuth docs.) jsonUrl$ = "https://api.twitter.com/1/statuses/user_timeline.json?screen_name=" + item.twitterUserName$ url = newTextParameterValue(jsonUrl$) endif endif liveDataFeed = newLiveDataFeedFromOldDataFormat(bsp, url, zoneHSM.rssDownloadPeriodicValue%) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) item.liveDataFeed = liveDataFeed item.rssTitle$ = item.liveDataFeed.name$ item.isRSSFeed = true if isTwitterFeed then liveDataFeed.isJSON = true else liveDataFeed.isJSON = false endif return item End Function Sub newBackgroundImagePlaylistItem(bsp As Object, playlistItemXML As Object, state As Object, playlistItemBS As Object) newMediaPlaylistItem(bsp, playlistItemXML, state, playlistItemBS) state.HStateEventHandler = STDisplayingBackgroundImageEventHandler End Sub Function GetViewModeValue(viewModeSpec$ As String) As Integer viewMode% = 2 if viewModeSpec$ = "Scale to Fill" then viewMode% = 0 else if viewModeSpec$ = "Letterboxed and Centered" then viewMode% = 1 endif return viewMode% End Function Function GetAudioOutputValue(audioOutputSpec$ As String) As Integer audioOutput% = 0 if audioOutputSpec$ = "USB Audio" then audioOutput% = 1 else if audioOutputSpec$ = "SPDIF Audio with Stereo PCM (HDMI Audio)" then audioOutput% = 2 else if audioOutputSpec$ = "SPDIF Audio, Raw Multichannel" then audioOutput% = 3 else if audioOutputSpec$ = "Analog Audio with Raw Multichannel on SPDIF" then audioOutput% = 4 endif return audioOutput% End Function Function GetAudioModeValue(audioModeSpec$ As String) As Integer audioMode% = 0 if audioModeSpec$ = "Multichannel Mixed Down to Stereo" then audioMode% = 1 else if audioModeSpec$ = "No Audio" then audioMode% = 2 else if audioModeSpec$ = "Mono Left Mixdown" then audioMode% = 3 else if audioModeSpec$ = "Mono Right Mixdown" then audioMode% = 4 endif return audioMode% End Function Function GetAudioMappingValue(audioMappingSpec$ As String) As Integer audioMapping% = 0 if audioMappingSpec$ = "Audio-2" then audioMapping% = 1 else if audioMappingSpec$ = "Audio-3" then audioMapping% = 2 endif return audioMapping% End Function Function GetAudioMappingSpan(audioOutput% As Integer, audioMappingSpec$ As String) As Integer audioMappingSpan% = 1 if audioOutput% = 0 and audioMappingSpec$ = "Audio-all" then audioMappingSpan% = 3 endif return audioMappingSpan% End Function Function GetImageModeValue(imageModeSpec$ As String) As Integer imageMode% = 1 if imageModeSpec$ = "Center Image" then imageMode% = 0 else if imageModeSpec$ = "Scale to Fill and Crop" then imageMode% = 2 else if imageModeSpec$ = "Scale to Fill" then imageMode% = 3 endif return imageMode% End Function Function GetSlideTransitionValue(slideTransitionSpec$ As String) As Integer slideTransition% = 0 if slideTransitionSpec$ = "Image wipe from top" then slideTransition% = 1 else if slideTransitionSpec$ = "Image wipe from bottom" then slideTransition% = 2 else if slideTransitionSpec$ = "Image wipe from left" then slideTransition% = 3 else if slideTransitionSpec$ = "Image wipe from right" then slideTransition% = 4 else if slideTransitionSpec$ = "Explode from center" then slideTransition% = 5 else if slideTransitionSpec$ = "Explode from top left" then slideTransition% = 6 else if slideTransitionSpec$ = "Explode from top right" then slideTransition% = 7 else if slideTransitionSpec$ = "Explode from bottom left" then slideTransition% = 8 else if slideTransitionSpec$ = "Explode from bottom right" then slideTransition% = 9 else if slideTransitionSpec$ = "Venetian blinds - vertical" then slideTransition% = 10 else if slideTransitionSpec$ = "Venetian blinds - horizontal" then slideTransition% = 11 else if slideTransitionSpec$ = "Comb effect - vertical" then slideTransition% = 12 else if slideTransitionSpec$ = "Comb effect - horizontal" then slideTransition% = 13 else if slideTransitionSpec$ = "Fade to background color" then slideTransition% = 14 else if slideTransitionSpec$ = "Fade to new image" then slideTransition% = 15 else if slideTransitionSpec$ = "Slide from top" then slideTransition% = 16 else if slideTransitionSpec$ = "Slide from bottom" then slideTransition% = 17 else if slideTransitionSpec$ = "Slide from left" then slideTransition% = 18 else if slideTransitionSpec$ = "Slide from right" then slideTransition% = 19 endif return slideTransition% End Function 'endregion 'region BSP Methods ' ************************************************* ' ' BSP Methods ' ' ************************************************* Sub InitializeTouchScreen(zone As Object) if type(m.touchScreen) <> "roTouchScreen" then m.touchScreen = CreateObject("roTouchScreen") m.touchScreen.SetPort(m.msgPort) REM Puts up a cursor if a mouse is attached REM The cursor must be a 32 x 32 BMP REM The x,y position is the “hot spot” point m.touchScreen.SetCursorBitmap("cursor.bmp", 16, 16) videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode = invalid m.touchScreen.SetResolution(resX, resY) m.touchScreen.SetCursorPosition(resX / 2, resY / 2) endif if type(zone.enabledRegions) <> "roList" then zone.enabledRegions = CreateObject("roList") endif End Sub Sub AddRectangularTouchRegion(zone As Object, touchEvent As Object, eventNum% As Integer) x% = touchEvent.x% + zone.x% y% = touchEvent.y% + zone.y% m.touchScreen.AddRectangleRegion(x%, y%, touchEvent.width%, touchEvent.height%, eventNum%) m.touchScreen.EnableRegion(eventNum%, false) End Sub Sub SetTouchRegions(state As Object) zone = state.stateMachine REM Display the cursor if there is a touch event active in this state REM If there is only one touch event we assume that it is to exit and don't display the cursor if type(m.touchScreen) <> "roTouchScreen" return ' clear out all regions in the active zone if type(zone.enabledRegions) = "roList" then for each eventNum in zone.enabledRegions m.touchScreen.EnableRegion(eventNum, false) next zone.enabledRegions.Clear() endif numTouchRegions% = 0 if type(state.touchEvents) = "roAssociativeArray" then for each eventNum in state.touchEvents m.touchScreen.EnableRegion(val(eventNum), true) zone.enabledRegions.AddTail(val(eventNum)) numTouchRegions% = numTouchRegions% + 1 next endif if state.type$ = "html5" and state.displayCursor then m.touchScreen.EnableCursor(true) m.diagnostics.PrintDebug("Html5 state - Cursor enabled") else if m.sign.touchCursorDisplayMode$ = "auto" then if numTouchRegions% > 1 then m.touchScreen.EnableCursor(true) m.diagnostics.PrintDebug("Cursor enabled") else m.touchScreen.EnableCursor(false) m.diagnostics.PrintDebug("Cursor disabled") endif else if m.sign.touchCursorDisplayMode$ = "display" and m.sign.numTouchEvents% > 0 then m.touchScreen.EnableCursor(true) m.diagnostics.PrintDebug("Cursor enabled") else m.touchScreen.EnableCursor(false) m.diagnostics.PrintDebug("Cursor disabled") endif return End Sub ' call this function to determine whether or not it is suitable to send the current command to a video player ' the heuristics are as follows: if the current zone type is AudioOnly or EnhancedAudio and there are audio items ' in the zone, then don't send the current command to a video player Function SendCommandToVideo() As Boolean if m.type$ = "AudioOnly" or m.type$ = "EnhancedAudio" then for each stateName in m.stateTable state = m.stateTable[stateName] if (state.type$ = "audio") or (state.type$ = "audioIn") or (state.type$ = "playFile" and state.mediaType$ = "audio") or (state.type$ = "mediaList" and state.mediaType$ = "audio") then return false endif next endif return true End Function Sub MapDigitalOutput(player As Object, parameters As Object) parameter = parameters["mapping"] digitalOutput$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Map digital output " + digitalOutput$) if type(player) <> "roInvalid" then player.MapDigitalOutput(int(val(digitalOutput$))) endif End Sub Sub SetAudioVolumeLimits(zone As Object, audioSettings As Object) if m.sysInfo.modelSupportsRoAudioOutput and zone.presentationUsesRoAudioOutputParameters and m.currentPresentationUsesRoAudioOutputParameters then audioSettings.minVolume% = zone.minimumVolume% audioSettings.maxVolume% = zone.maximumVolume% else audioOutput% = audioSettings.audioOutput% stereoMapping% = audioSettings.stereoMapping% ANALOG_AUDIO = 0 USB_AUDIO = 1 DIGITAL_AUDIO_STEREO_PCM = 2 DIGITAL_AUDIO_RAW_AC3 = 3 ANALOG_HDMI_RAW_AC3 = 4 if audioOutput% = ANALOG_AUDIO or audioOutput% = ANALOG_HDMI_RAW_AC3 then if stereoMapping% = 0 then audioSettings.minVolume% = m.sign.audio1MinVolume% audioSettings.maxVolume% = m.sign.audio1MaxVolume% else if stereoMapping% = 1 then audioSettings.minVolume% = m.sign.audio2MinVolume% audioSettings.maxVolume% = m.sign.audio2MaxVolume% else audioSettings.minVolume% = m.sign.audio3MinVolume% audioSettings.maxVolume% = m.sign.audio3MaxVolume% endif else if audioOutput% = USB_AUDIO then audioSettings.minVolume% = m.sign.usbMinVolume% audioSettings.maxVolume% = m.sign.usbMaxVolume% else if audioOutput% = DIGITAL_AUDIO_STEREO_PCM then audioSettings.minVolume% = m.sign.hdmiMinVolume% audioSettings.maxVolume% = m.sign.hdmiMaxVolume% else audioSettings.minVolume% = 0 audioSettings.maxVolume% = 100 endif endif End Sub Sub SetAudioMode1(parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["mode"] mode$ = parameter.GetCurrentParameterValue() zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if lcase(mode$) = "passthrough" then mode% = 0 else if lcase(mode$) = "left" then mode% = 3 else if lcase(mode$) = "right" then mode% = 4 else mode% = 1 endif if type(zone.videoPlayer) = "roVideoPlayer" then zone.videoPlayer.SetAudioMode(mode%) endif if IsAudioPlayer(zone.audioPlayer) then zone.audioPlayer.SetAudioMode(mode%) endif endif End Sub Sub SetAllAudioOutputs(parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["analog"] analog$ = parameter.GetCurrentParameterValue() if parameters.DoesExist("analog2") then parameter = parameters["analog2"] analog2$ = parameter.GetCurrentParameterValue() else analog2$ = "none" endif if parameters.DoesExist("analog3") then parameter = parameters["analog3"] analog3$ = parameter.GetCurrentParameterValue() else analog3$ = "none" endif parameter = parameters["hdmi"] hdmi$ = parameter.GetCurrentParameterValue() parameter = parameters["spdif"] spdif$ = parameter.GetCurrentParameterValue() if parameters.DoesExist("usb") then parameter = parameters["usb"] usb$ = parameter.GetCurrentParameterValue() else usb$ = "none" endif pcm = CreateObject("roArray", 1, true) compressed = CreateObject("roArray", 1, true) multichannel = CreateObject("roArray", 1, true) analogAudioOutput = CreateObject("roAudioOutput", "Analog:1") analog2AudioOutput = CreateObject("roAudioOutput", "Analog:2") analog3AudioOutput = CreateObject("roAudioOutput", "Analog:3") hdmiAudioOutput = CreateObject("roAudioOutput", "HDMI") spdifAudioOutput = CreateObject("roAudioOutput", "SPDIF") usbAudioOutput = CreateObject("roAudioOutput", "USB") if lcase(analog$) <> "none" and lcase(analog$) <> "multichannel" then pcm.push(analogAudioOutput) endif if lcase(analog2$) = "pcm" then pcm.push(analog2AudioOutput) endif if lcase(analog3$) = "pcm" then pcm.push(analog3AudioOutput) endif if lcase(analog$)="multichannel" then multichannel.push(analogAudioOutput) else if lcase(analog2$)="multichannel" then multichannel.push(analog2AudioOutput) else if lcase(analog3$)="multichannel" then multichannel.push(analog3AudioOutput) endif if lcase(hdmi$) = "passthrough" then compressed.push(hdmiAudioOutput) else if lcase(hdmi$) <> "none" then pcm.push(hdmiAudioOutput) endif if lcase(spdif$) = "passthrough" then compressed.push(spdifAudioOutput) else if lcase(spdif$) <> "none" then pcm.push(spdifAudioOutput) endif if lcase(usb$) = "pcm" then pcm.push(usbAudioOutput) else if lcase(usb$) = "multichannel" then multichannel.push(usbAudioOutput) endif if pcm.Count() = 0 then noPCMAudioOutput = CreateObject("roAudioOutput", "none") pcm.push(noPCMAudioOutput) endif if compressed.Count() = 0 then noCompressedAudioOutput = CreateObject("roAudioOutput", "none") compressed.push(noCompressedAudioOutput) endif if multichannel.Count() = 0 then noMultichannelAudioOutput = CreateObject("roAudioOutput", "none") multichannel.push(noMultichannelAudioOutput) endif zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then zone.videoPlayer.SetPcmAudioOutputs(pcm) zone.videoPlayer.SetCompressedAudioOutputs(compressed) zone.videoPlayer.SetMultichannelAudioOutputs(multichannel) endif if IsAudioPlayer(zone.audioPlayer) then zone.audioPlayer.SetPcmAudioOutputs(pcm) zone.audioPlayer.SetCompressedAudioOutputs(compressed) zone.audioPlayer.SetMultichannelAudioOutputs(multichannel) endif endif End Sub Sub UnmuteAudioConnector(connector$ As String) audioOutput = CreateObject("roAudioOutput", connector$) if type(audioOutput) = "roAudioOutput" then audioOutput.SetMute(false) endif End Sub Sub UnmuteAllAudio() if m.sysInfo.modelSupportsRoAudioOutput then m.UnmuteAudioConnector("Analog:1") m.UnmuteAudioConnector("Analog:2") m.UnmuteAudioConnector("Analog:3") m.UnmuteAudioConnector("HDMI") m.UnmuteAudioConnector("SPDIF") m.UnmuteAudioConnector("USB") endif End Sub Sub MuteAudioOutput(muteOn as Boolean, parameters As Object, parameterName$ As String, objectName$ As String) if parameters.DoesExist(parameterName$) then parameter = parameters[parameterName$] mute$ = parameter.GetCurrentParameterValue() if lcase(mute$) = "true" then audioOutput = CreateObject("roAudioOutput", objectName$) if type(audioOutput) = "roAudioOutput" then audioOutput.SetMute(muteOn) endif endif endif End Sub Sub MuteAudioOutputs(muteOn as Boolean, parameters As Object) m.MuteAudioOutput(muteOn, parameters, "analog", "Analog:1") m.MuteAudioOutput(muteOn, parameters, "analog2", "Analog:2") m.MuteAudioOutput(muteOn, parameters, "analog3", "Analog:3") m.MuteAudioOutput(muteOn, parameters, "hdmi", "HDMI") m.MuteAudioOutput(muteOn, parameters, "spdif", "SPDIF") m.MuteAudioOutput(muteOn, parameters, "usb", "USB") End Sub Sub SetConnectorVolume(parameters As Object) parameter = parameters["connector"] connector$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volume$ = parameter.GetCurrentParameterValue() volume% = int(val(volume$)) if lcase(connector$) = "analog" then audioOutput = CreateObject("roAudioOutput", "Analog:1") m.analogVolume% = volume% else if lcase(connector$) = "analog2" then audioOutput = CreateObject("roAudioOutput", "Analog:2") m.analog2Volume% = volume% else if lcase(connector$) = "analog3" then audioOutput = CreateObject("roAudioOutput", "Analog:3") m.analog3Volume% = volume% else if lcase(connector$) = "hdmi" then audioOutput = CreateObject("roAudioOutput", "HDMI") m.hdmiVolume% = volume% else if lcase(connector$) = "spdif" then audioOutput = CreateObject("roAudioOutput", "SPDIF") m.spdifVolume% = volume% else if lcase(connector$) = "usb" then audioOutput = CreateObject("roAudioOutput", "USB") m.usbVolume% = volume% endif if type(audioOutput) = "roAudioOutput" then audioOutput.SetVolume(volume%) endif End Sub Sub ChangeConnectorVolume(multiplier% As Integer, parameters As Object) parameter = parameters["connector"] connector$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volumeDelta$ = parameter.GetCurrentParameterValue() volumeDelta% = int(val(volumeDelta$)) * multiplier% if lcase(connector$) = "analog" then m.analogVolume% = ExecuteChangeConnectorVolume("Analog:1", m.analogVolume% + volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) else if lcase(connector$) = "analog2" then m.analog2Volume% = ExecuteChangeConnectorVolume("Analog:2", m.analog2Volume% + volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) else if lcase(connector$) = "analog3" then m.analog3Volume% = ExecuteChangeConnectorVolume("Analog:3", m.analog3Volume% + volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) else if lcase(connector$) = "hdmi" then m.hdmiVolume% = ExecuteChangeConnectorVolume("HDMI", m.hdmiVolume% + volumeDelta%, m.sign.hdmiMinVolume%, m.sign.hdmiMaxVolume%) else if lcase(connector$) = "spdif" then m.spdifVolume% = ExecuteChangeConnectorVolume("SPDIF", m.spdifVolume% + volumeDelta%, m.sign.spdifMinVolume%, m.sign.spdifMaxVolume%) else if lcase(connector$) = "usb" then m.usbVolume% = ExecuteChangeConnectorVolume("USB", m.usbVolume% + volumeDelta%, m.sign.usbMinVolume%, m.sign.usbMaxVolume%) endif End Sub Function ExecuteChangeConnectorVolume(connector$ As String, newVolume% As Integer, minVolume% As Integer, maxVolume% As Integer) As Integer audioOutput = CreateObject("roAudioOutput", connector$) if newVolume% > maxVolume% then newVolume% = maxVolume% else if newVolume% < minVolume% newVolume% = minVolume% endif audioOutput.SetVolume(newVolume%) ' print "ExecuteChangeConnectorVolume - connector = ";connector$;", volume = ";newVolume% return newVolume% End Function Sub SetZoneVolume(parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volume$ = parameter.GetCurrentParameterValue() volume% = int(val(volume$)) zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then zone.videoPlayer.SetVolume(volume%) for i% = 0 to 5 zone.videoChannelVolumes[i%] = volume% next endif if IsAudioPlayer(zone.audioPlayer) then zone.audioPlayer.SetVolume(volume%) for i% = 0 to 5 zone.audioChannelVolumes[i%] = volume% next endif endif End Sub Sub ChangeZoneVolume(multiplier% As Integer, parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volumeDelta$ = parameter.GetCurrentParameterValue() volumeDelta% = int(val(volumeDelta$)) * multiplier% zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then if multiplier% > 0 then minVolume% = zone.videoPlayerAudioSettings.minVolume% maxVolume% = zone.videoPlayerAudioSettings.maxVolume% else minVolume% = zone.videoPlayerAudioSettings.minVolume% maxVolume% = zone.videoPlayerAudioSettings.maxVolume% endif m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 63, volumeDelta%, minVolume%, maxVolume%) endif if IsAudioPlayer(zone.audioPlayer) then if multiplier% > 0 then minVolume% = zone.audioPlayerAudioSettings.minVolume% maxVolume% = zone.audioPlayerAudioSettings.maxVolume% else minVolume% = zone.audioPlayerAudioSettings.minVolume% maxVolume% = zone.audioPlayerAudioSettings.maxVolume% endif m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 63, volumeDelta%, minVolume%, maxVolume%) endif endif End Sub Sub SetZoneChannelVolume(parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["channel"] channelMask$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volume$ = parameter.GetCurrentParameterValue() volume% = int(val(volume$)) zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then player = zone.videoPlayer channelVolumes = zone.videoChannelVolumes else if IsAudioPlayer(zone.audioPlayer) then player = zone.audioPlayer channelVolumes = zone.audioChannelVolumes endif m.SetChannelVolumes(player, channelVolumes, int(val(channelMask$)), int(val(volume$))) endif End Sub Sub ChangeZoneChannelVolume(multiplier% As Integer, parameters As Object) parameter = parameters["zoneId"] zoneId$ = parameter.GetCurrentParameterValue() parameter = parameters["channel"] channelMask$ = parameter.GetCurrentParameterValue() parameter = parameters["volume"] volumeDelta$ = parameter.GetCurrentParameterValue() volumeDelta% = int(val(volumeDelta$)) * multiplier% zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then player = zone.videoPlayer channelVolumes = zone.videoChannelVolumes minVolume% = zone.videoPlayerAudioSettings.minVolume% maxVolume% = zone.videoPlayerAudioSettings.maxVolume% else if IsAudioPlayer(zone.audioPlayer) then player = zone.audioPlayer channelVolumes = zone.audioChannelVolumes minVolume% = zone.audioPlayerAudioSettings.minVolume% maxVolume% = zone.audioPlayerAudioSettings.maxVolume% endif m.ChangeChannelVolumes(player, channelVolumes, int(val(channelMask$)), volumeDelta%, minVolume%, maxVolume%) endif End Sub Sub SetAudioOutput(zone As Object, useVideoPlayer As Boolean, parameters As Object) parameter = parameters["output"] audioOutput$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set audio output " + audioOutput$) audioOutput% = int(val(audioOutput$)) player = invalid if useVideoPlayer then zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then player = zone.videoPlayer zone.videoPlayerAudioSettings.audioOutput% = audioOutput% if audioOutput% <> 0 then zone.videoPlayerAudioSettings.audioMappingSpan% = 1 endif m.SetAudioVolumeLimits(zone, zone.videoPlayerAudioSettings) endif else if IsAudioPlayer(zone.audioPlayer) then player = zone.audioPlayer zone.audioPlayerAudioSettings.audioOutput% = audioOutput% if audioOutput% <> 0 then zone.audioPlayerAudioSettings.audioMappingSpan% = 1 endif m.SetAudioVolumeLimits(zone, zone.audioPlayerAudioSettings) endif if type(player) = "roVideoPlayer" or IsAudioPlayer(player) then player.SetAudioOutput(int(val(audioOutput$))) endif End Sub Sub SetAudioMode(player As Object, parameters As Object) parameter = parameters["mode"] audioMode$ = parameter.GetCurrentParameterValue() if audioMode$ <> "" then m.diagnostics.PrintDebug("Set audio mode " + audioMode$) if type(player) <> "roInvalid" then player.SetAudioMode(int(val(audioMode$))) endif endif End Sub Sub MapStereoOutput(zone As Object, useVideoPlayer As Boolean, parameters As Object) parameter = parameters["mapping"] mapping$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Map stereo output " + mapping$) mapping% = 0 spanning% = 1 if mapping$ = "onboard-audio2" then mapping% = 1 else if mapping$ = "onboard-audio3" then mapping% = 2 else if mapping$ = "onboard-audio-all" then spanning% = 3 endif ' if m.sysInfo.expanderPresent then ' mapping% = mapping% + 3 ' endif player = invalid if useVideoPlayer then zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then player = zone.videoPlayer zone.videoPlayerAudioSettings.stereoMapping% = mapping% zone.videoPlayerAudioSettings.audioMappingSpan% = spanning% m.SetAudioVolumeLimits(zone, zone.videoPlayerAudioSettings) endif else if IsAudioPlayer(zone.audioPlayer) then player = zone.audioPlayer zone.audioPlayerAudioSettings.stereoMapping% = mapping% zone.audioPlayerAudioSettings.audioMappingSpan% = spanning% m.SetAudioVolumeLimits(zone, zone.audioPlayerAudioSettings) endif if type(player) = "roVideoPlayer" or IsAudioPlayer(player) then player.MapStereoOutput(mapping%) player.SetStereoMappingSpan(spanning%) endif End Sub Sub SetSpdifMute(player As Object, parameters As Object) parameter = parameters["mute"] muteOn$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set SPDIF Mute " + muteOn$) if type(player) <> "roInvalid" then player.SetSpdifMute(int(val(muteOn$))) endif End Sub Sub SetAnalogMute(channelVolumes As Object, player As Object, parameters As Object) parameter = parameters["mute"] muteOn$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set Analog Mute " + muteOn$) if type(player) <> "roInvalid" and type(channelVolumes) = "roArray" then muteOn% = int(val(muteOn$)) if muteOn% = 0 then for i% = 0 to 5 mask% = 2 ^ i% player.SetChannelVolumes(mask%, channelVolumes[i%]) next else player.SetChannelVolumes(63, 0) endif endif End Sub Sub SetHDMIMute(parameters As Object) parameter = parameters["mute"] muteOn$ = parameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set HDMI Mute " + muteOn$) videoMode = CreateObject("roVideoMode") if muteOn$ = "1" then disableHDMIAudio = true else disableHDMIAudio = false endif videoMode.HdmiAudioDisable(disableHDMIAudio) videoMode = invalid End Sub Sub SetVideoVolume(zone As Object, parameter$ As String) volume% = int(val(parameter$)) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then zone.videoPlayer.SetVolume(volume%) for i% = 0 to 5 zone.videoChannelVolumes[i%] = volume% next endif End Sub Sub SetVideoVolumeByConnector(zone As Object, output$ As String, volume$ As String) volume% = int(val(volume$)) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then if zone.videoPlayerAudioSettings.audioMappingSpan% = 3 then if output$ = "onboard-audio1" then channelMask% = 3 zone.videoChannelVolumes[0] = volume% zone.videoChannelVolumes[1] = volume% else if output$ = "onboard-audio2" then channelMask% = 12 zone.videoChannelVolumes[2] = volume% zone.videoChannelVolumes[3] = volume% else if output$ = "onboard-audio3" then channelMask% = 48 zone.videoChannelVolumes[4] = volume% zone.videoChannelVolumes[5] = volume% else channelMask% = 63 for i% = 0 to 5 zone.videoChannelVolumes[i%] = volume% next endif else channelMask% = 63 for i% = 0 to 5 zone.videoChannelVolumes[i%] = volume% next endif ' if analogOutput$ = "onboard-audio1" then ' channelMask% = 3 ' zone.videoChannelVolumes[0] = volume% ' zone.videoChannelVolumes[1] = volume% ' else if analogOutput$ = "onboard-audio2" then ' channelMask% = 12 ' zone.videoChannelVolumes[2] = volume% ' zone.videoChannelVolumes[3] = volume% ' else if analogOutput$ = "onboard-audio3" then ' channelMask% = 48 ' zone.videoChannelVolumes[4] = volume% ' zone.videoChannelVolumes[5] = volume% ' else ' channelMask% = 63 ' for i% = 0 to 5 ' zone.videoChannelVolumes[i%] = volume% ' next ' endif zone.videoPlayer.SetChannelVolumes(channelMask%, volume%) endif End Sub Sub SetVideoChannnelVolume(zone As Object, channelMask$ As String, volume$ As String) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then m.SetChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, int(val(channelMask$)), int(val(volume$))) endif End Sub Sub IncrementVideoChannnelVolumes(zone As Object, channelMask$ As String, volumeDelta$ As String) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then channelMask% = int(val(channelMask$)) m.ChangeVideoVolume(zone, channelMask%, int(val(volumeDelta$)), zone.videoPlayerAudioSettings.minVolume%, zone.videoPlayerAudioSettings.maxVolume%) endif End Sub Sub DecrementVideoChannnelVolumes(zone As Object, channelMask$ As String, volumeDelta$ As String) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then channelMask% = int(val(channelMask$)) delta% = int(val(volumeDelta$)) m.ChangeVideoVolume(zone, channelMask%, -delta%, zone.videoPlayerAudioSettings.minVolume%, zone.videoPlayerAudioSettings.maxVolume%) endif End Sub Sub SetAudioVolume(zone As Object, parameter$ As String) volume% = int(val(parameter$)) if type(zone) = "roAssociativeArray" then if IsAudioPlayer(zone.audioPlayer) then zone.audioPlayer.SetVolume(volume%) for i% = 0 to 5 zone.audioChannelVolumes[i%] = volume% next endif endif End Sub Sub SetAudioVolumeByConnector(zone As Object, output$ As String, volume$ As String) if type(zone) = "roAssociativeArray" then if IsAudioPlayer(zone.audioPlayer) then volume% = int(val(volume$)) if zone.audioPlayerAudioSettings.audioMappingSpan% = 3 then if output$ = "onboard-audio1" then channelMask% = 3 zone.audioChannelVolumes[0] = volume% zone.audioChannelVolumes[1] = volume% else if output$ = "onboard-audio2" then channelMask% = 12 zone.audioChannelVolumes[2] = volume% zone.audioChannelVolumes[3] = volume% else if output$ = "onboard-audio3" then channelMask% = 48 zone.audioChannelVolumes[4] = volume% zone.audioChannelVolumes[5] = volume% else channelMask% = 63 for i% = 0 to 5 zone.audioChannelVolumes[i%] = volume% next endif else channelMask% = 63 for i% = 0 to 5 zone.audioChannelVolumes[i%] = volume% next endif ' if analogOutput$ = "onboard-audio1" then ' channelMask% = 3 ' zone.audioChannelVolumes[0] = volume% ' zone.audioChannelVolumes[1] = volume% ' else if analogOutput$ = "onboard-audio2" then ' channelMask% = 12 ' zone.audioChannelVolumes[2] = volume% ' zone.audioChannelVolumes[3] = volume% ' else if analogOutput$ = "onboard-audio3" then ' channelMask% = 48 ' zone.audioChannelVolumes[4] = volume% ' zone.audioChannelVolumes[5] = volume% ' else ' channelMask% = 63 ' for i% = 0 to 5 ' zone.audioChannelVolumes[i%] = volume% ' next ' endif zone.audioPlayer.SetChannelVolumes(channelMask%, volume% ) endif endif End Sub Sub SetAudioChannnelVolume(zone As Object, channelMask$ As String, volume$ As String) if type(zone) = "roAssociativeArray" then if IsAudioPlayer(zone.audioPlayer) then m.SetChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, int(val(channelMask$)), int(val(volume$))) endif endif End Sub Sub IncrementAudioVolume(zone As Object, parameter$ As String, maxVolume% As Integer) m.ChangeAudioVolume(zone, 63, int(val(parameter$)), 0, maxVolume%) End Sub Sub DecrementAudioVolume(zone As Object, parameter$ As String, minVolume% As Integer) delta% = int(val(parameter$)) m.ChangeAudioVolume(zone, 63, -delta%, minVolume%, 100) End Sub Sub SetChannelVolumes(player As Object, channelVolumes As Object, channelMask% As Integer, volume% As Integer) for i% = 0 to 5 mask% = 2 ^ i% if channelMask% and mask% then channelVolumes[i%] = volume% player.SetChannelVolumes(mask%, channelVolumes[i%]) ' print "SetChannelVolumes - mask = ";mask%;", volume = ";channelVolumes[i%] endif next End Sub Function GetVideoZone(zone As Object) As Object if type(zone) = "roAssociativeArray" then if type(zone.videoPlayer) = "roVideoPlayer" then return zone endif endif if type(m.sign) = "roAssociativeArray" then if type(m.sign.videoZoneHSM) = "roAssociativeArray" and type(m.sign.videoZoneHSM.videoPlayer) = "roVideoPlayer" then return m.sign.videoZoneHSM endif endif return invalid End Function Function GetZone(zoneId$ As String) As Object for each zone in m.sign.zonesHSM if zone.id$ = zoneId$ then return zone endif next return invalid End Function Sub ChangeChannelVolumes(player As Object, channelVolumes As Object, channelMask% As Integer, delta% As Integer, minVolume% As Integer, maxVolume% As Integer) for i% = 0 to 5 mask% = 2 ^ i% if channelMask% and mask% then channelVolumes[i%] = channelVolumes[i%] + delta% if channelVolumes[i%] > maxVolume% then channelVolumes[i%] = maxVolume% else if channelVolumes[i%] < minVolume% then channelVolumes[i%] = minVolume% endif player.SetChannelVolumes(mask%, channelVolumes[i%]) ' print "SetChannelVolumes - mask = ";mask%;", volume = ";channelVolumes[i%] endif next End Sub Sub ChangeVideoVolumeByConnector(zone As Object, output$ As String, volumeDelta% As Integer) if type(zone) = "roAssociativeArray" then if zone.videoPlayerAudioSettings.audioMappingSpan% = 3 then if output$ = "onboard-audio1" then m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) else if output$ = "onboard-audio2" then m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) else if output$ = "onboard-audio3" then m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) else m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) endif ' else if zone.videoPlayerAudioSettings.audioOutput% = 0 then ' m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) ' m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) ' m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) else m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, 63, volumeDelta%, zone.videoPlayerAudioSettings.minVolume%, zone.videoPlayerAudioSettings.maxVolume%) endif endif End Sub Sub IncrementVideoVolumeByConnector(zone As Object, output$ As String, volumeDelta$ As String) zone = m.GetVideoZone(zone) delta% = int(val(volumeDelta$)) m.ChangeVideoVolumeByConnector(zone, output$, delta%) End Sub Sub DecrementVideoVolumeByConnector(zone As Object, output$ As String, volumeDelta$ As String) zone = m.GetVideoZone(zone) delta% = int(val(volumeDelta$)) m.ChangeVideoVolumeByConnector(zone, output$, -delta%) End Sub Sub ChangeVideoVolume(zone As Object, channelMask% as Integer, delta% As Integer, minVolume% As Integer, maxVolume% As Integer) m.ChangeChannelVolumes(zone.videoPlayer, zone.videoChannelVolumes, channelMask%, delta%, minVolume%, maxVolume%) End Sub Sub IncrementVideoVolume(zone As Object, volumeDelta$ As String) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then m.ChangeVideoVolume(zone, 63, int(val(volumeDelta$)), 0, zone.videoPlayerAudioSettings.maxVolume%) endif End Sub Sub DecrementVideoVolume(zone As Object, volumeDelta$ As String) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then delta% = int(val(volumeDelta$)) m.ChangeVideoVolume(zone, 63, -delta%, zone.videoPlayerAudioSettings.minVolume%, 100) endif End Sub Sub ChangeAudioVolumeByConnector(zone As Object, output$ As String, volumeDelta% As Integer) if type(zone) = "roAssociativeArray" then if IsAudioPlayer(zone.audioPlayer) then if zone.audioPlayerAudioSettings.audioMappingSpan% = 3 then if output$ = "onboard-audio1" then m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) else if output$ = "onboard-audio2" then m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) else if output$ = "onboard-audio3" then m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) else m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) endif ' else if zone.audioPlayerAudioSettings.audioOutput% = 0 then ' m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 3, volumeDelta%, m.sign.audio1MinVolume%, m.sign.audio1MaxVolume%) ' m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 12, volumeDelta%, m.sign.audio2MinVolume%, m.sign.audio2MaxVolume%) ' m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 48, volumeDelta%, m.sign.audio3MinVolume%, m.sign.audio3MaxVolume%) else m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, 63, volumeDelta%, zone.audioPlayerAudioSettings.minVolume%, zone.audioPlayerAudioSettings.maxVolume%) endif endif endif End Sub Sub IncrementAudioVolumeByConnector(zone As Object, output$ As String, volumeDelta$ As String) m.ChangeAudioVolumeByConnector(zone, output$, int(val(volumeDelta$))) End Sub Sub DecrementAudioVolumeByConnector(zone As Object, output$ As String, volumeDelta$ As String) delta% = int(val(volumeDelta$)) m.ChangeAudioVolumeByConnector(zone, output$, -delta%) End Sub Sub ChangeAudioVolume(zone As Object, channelMask% as Integer, delta% As Integer, minVolume% As Integer, maxVolume% As Integer) if type(zone) = "roAssociativeArray" then if IsAudioPlayer(zone.audioPlayer) then m.ChangeChannelVolumes(zone.audioPlayer, zone.audioChannelVolumes, channelMask%, delta%, minVolume%, maxVolume%) endif endif End Sub Sub IncrementAudioChannelVolumes(zone As Object, channelMask$ As String, volumeDelta$ As String) if IsAudioPlayer(zone.audioPlayer) then channelMask% = int(val(channelMask$)) m.ChangeAudioVolume(zone, channelMask%, int(val(volumeDelta$)), zone.audioPlayerAudioSettings.minVolume%, zone.audioPlayerAudioSettings.maxVolume%) endif End Sub Sub DecrementAudioChannelVolumes(zone As Object, channelMask$ As String, volumeDelta$ As String) if IsAudioPlayer(zone.audioPlayer) then channelMask% = int(val(channelMask$)) delta% = int(val(volumeDelta$)) m.ChangeAudioVolume(zone, channelMask%, -delta%, zone.audioPlayerAudioSettings.minVolume%, zone.audioPlayerAudioSettings.maxVolume%) endif End Sub Sub ConfigureAudioResources() if not(m.bsp.sysInfo.deviceFamily$ = "monaco" or m.bsp.sysInfo.deviceFamily$ = "pandora3" or m.bsp.sysInfo.deviceFamily$ = "apollo" or m.bsp.sysInfo.deviceFamily$ = "bpollo") then if type(m.videoPlayer) = "roVideoPlayer" then m.videoPlayer.ConfigureAudioResources() else if IsAudioPlayer(m.audioPlayer) then m.audioPlayer.ConfigureAudioResources() endif endif End Sub Sub CecDisplayOn() m.SendCecCommand("400D") End Sub Sub CecDisplayOff() m.SendCecCommand("4036") End Sub Sub CecPhilipsSetVolume(volume% As Integer) b = CreateObject("roByteArray") b[0] = volume% volumeAsAscii$ = b.ToHexString() b = invalid setVolume$ = "40A0000C3022" + volumeAsAscii$ SendCecCommand(setVolume$) End Sub Sub SendCecCommand(cecCommand$ As String) cec = CreateObject("roCecInterface") if type(cec) = "roCecInterface" then b = CreateObject("roByteArray") b.fromhexstring(cecCommand$) cec.SendRawMessage(b) cec = invalid endif End Sub Sub PauseVideo(zone As Object) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then zone.videoPlayer.Pause() endif End Sub Sub ResumeVideo(zone As Object) zone = m.GetVideoZone(zone) if type(zone) = "roAssociativeArray" then zone.videoPlayer.Resume() endif End Sub Sub SetPowerSaveMode(enablePowerSaveMode As Boolean) videoMode = CreateObject("roVideoMode") videoMode.SetPowerSaveMode(enablePowerSaveMode) videoMode = invalid End Sub Function GetAttachedFiles() As Object return m.additionalPublishedFiles End Function Function PostponeRestart() As Boolean if m.dontChangePresentationUntilMediaEndEventReceived then m.restartPendingMediaEnd = true endif return m.dontChangePresentationUntilMediaEndEventReceived End Function Function ProcessMediaEndEvent() As Boolean executeContentRestart = m.restartPendingMediaEnd if executeContentRestart then m.diagnostics.PrintDebug("ProcessMediaEndEvent - execute content update") m.restartPendingMediaEnd = false ' send internal message to prepare for restart prepareForRestartEvent = CreateObject("roAssociativeArray") prepareForRestartEvent["EventType"] = "PREPARE_FOR_RESTART" m.msgPort.PostMessage(prepareForRestartEvent) ' send internal message indicating that new content is available contentUpdatedEvent = CreateObject("roAssociativeArray") contentUpdatedEvent["EventType"] = "CONTENT_UPDATED" m.msgPort.PostMessage(contentUpdatedEvent) endif return executeContentRestart End Function 'endregion 'region Common Zone State Machine Methods ' ************************************************* ' ' Common Zone State Machine Methods ' ' ************************************************* Sub newZoneCommon(bsp As Object, zoneXML As Object, zoneHSM As Object) zoneHSM.audioPlayer = invalid zoneHSM.videoPlayer = invalid zoneHSM.name$ = zoneXML.name.GetText() zoneHSM.x% = int(val(zoneXML.x.GetText())) zoneHSM.y% = int(val(zoneXML.y.GetText())) zoneHSM.width% = int(val(zoneXML.width.GetText())) zoneHSM.height% = int(val(zoneXML.height.GetText())) zoneHSM.type$ = zoneXML.type.GetText() zoneHSM.id$ = zoneXML.id.GetText() zoneHSM.bsp = bsp zoneHSM.ConfigureAudioResources = ConfigureAudioResources zoneHSM.SetAudioOutputAndMode = SetAudioOutputAndMode zoneHSM.LogPlayStart = LogPlayStart zoneHSM.ClearImagePlane = ClearImagePlane zoneHSM.ShowImageWidget = ShowImageWidget zoneHSM.ShowCanvasWidget = ShowCanvasWidget zoneHSM.ShowHtmlWidget = ShowHtmlWidget zoneHSM.UpdateWidgetVisibility = UpdateWidgetVisibility zoneHSM.SendCommandToVideo = SendCommandToVideo zoneHSM.stTop = zoneHSM.newHState(bsp, "Top") zoneHSM.stTop.HStateEventHandler = STTopEventHandler zoneHSM.topState = zoneHSM.stTop End Sub Sub InitializeZoneCommon(msgPort As Object) zoneHSM = m zoneHSM.msgPort = msgPort zoneHSM.isVideoZone = false zoneHSM.preloadState = invalid zoneHSM.preloadedStateName$ = "" zoneHSM.rectangle = CreateObject("roRectangle", zoneHSM.x%, zoneHSM.y%, zoneHSM.width%, zoneHSM.height%) ' byte arrays to store stream byte input zoneHSM.serialStreamInputBuffers = CreateObject("roArray", 6, true) for i% = 0 to 5 zoneHSM.serialStreamInputBuffers[i%] = CreateObject("roByteArray") next End Sub 'endregion 'region MediaItem Methods ' ************************************************* ' ' MediaItem Methods ' ' ************************************************* Function GetNextStateName(transition As Object) As Object nextState = { } if type(transition.conditionalTargets) = "roArray" then for each conditionalTarget in transition.conditionalTargets matchFound = false currentValue% = val(conditionalTarget.userVariable.GetCurrentValue()) userVariableValue = conditionalTarget.userVariableValue.GetCurrentParameterValue() userVariableValue% = val(userVariableValue) if conditionalTarget.operator$ = "EQ" then if conditionalTarget.userVariable.GetCurrentValue() = userVariableValue then matchFound = true endif else if conditionalTarget.operator$ = "LT" then if currentValue% < userVariableValue% then matchFound = true endif else if conditionalTarget.operator$ = "LTE" then if currentValue% <= userVariableValue% then matchFound = true endif else if conditionalTarget.operator$ = "GT" then if currentValue% > userVariableValue% then matchFound = true endif else if conditionalTarget.operator$ = "GTE" then if currentValue% >= userVariableValue% then matchFound = true endif else if conditionalTarget.operator$ = "B" then userVariableValue2 = conditionalTarget.userVariableValue2.GetCurrentParameterValue() userVariableValue2% = val(userVariableValue2) if currentValue% >= userVariableValue% and currentValue% <= userVariableValue2% then matchFound = true endif endif if matchFound then if conditionalTarget.targetMediaStateIsPreviousState then nextState$ = m.stateMachine.previousStateName$ else nextState$ = conditionalTarget.targetMediaState$ endif nextState.nextState$ = nextState$ nextState.actualTarget = conditionalTarget return nextState endif next endif if transition.targetMediaStateIsPreviousState then nextState$ = m.stateMachine.previousStateName$ else nextState$ = transition.targetMediaState$ endif nextState.nextState$ = nextState$ nextState.actualTarget = transition return nextState End Function Sub UpdatePreviousCurrentStateNames() m.stateMachine.previousStateName$ = m.id$ End Sub Function GetAnyMediaRSSTransition() As Object transition = invalid ' support others? if type(m.signChannelEndEvent) = "roAssociativeArray" then transition = m.signChannelEndEvent else if type(m.mstimeoutEvent) = "roAssociativeArray" then transition = m.mstimeoutEvent endif return transition End Function Function ExecuteTransition(transition As Object, stateData As Object, payload$ As String) As String nextState$ = "init" while nextState$ <> "" ' before transitioning to next state, ensure that the transition is allowed nextState = m.GetNextStateName(transition) nextState$ = nextState.nextState$ actualTarget = nextState.actualTarget if nextState$ <> "" then nextState = m.stateMachine.stateTable[nextState$] if nextState.type$ = "mediaRSS" and nextState.rssURL$ = "" then ' skip an empty localized playlist m.bsp.diagnostics.PrintDebug("Unassigned local playlist " + nextState.name$ + " encountered, attempt to navigate to next state.") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_UNASSIGNED_LOCAL_PLAYLIST, nextState.name$) defaultTransition = nextState.GetAnyMediaRSSTransition() if defaultTransition <> invalid then transition = defaultTransition else ' no transition found - not sure what to do m.bsp.diagnostics.PrintDebug("Unable to navigate from unassigned local playlist " + nextState.name$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_UNASSIGNED_LOCAL_PLAYLIST_NO_NAVIGATION, nextState.name$) exit while endif else if nextState.type$ = "playFile" then if not nextState.filesTable.DoesExist(payload$) then m.bsp.diagnostics.PrintDebug("transition cancelled - payload " + payload$ + " not found in target state's table") return "HANDLED" else ' set payload$ member before ExecuteTransitionCommands is called - needed if there is a synchronize transition command nextState.payload$ = payload$ endif endif exit while endif endif end while switchToNewPresentation = m.bsp.ExecuteTransitionCommands(m.stateMachine, actualTarget) if switchToNewPresentation then return "HANDLED" endif if nextState$ = "" then return "HANDLED" else stateData.nextState = m.stateMachine.stateTable[nextState$] stateData.nextState.payload$ = payload$ m.UpdatePreviousCurrentStateNames() return "TRANSITION" endif End Function Sub AssignWildcardInputToUserVariable(bsp As Object, input$ As String) if type(m.variableToAssignFromWildcard) = "roAssociativeArray" then m.variableToAssignFromWildcard.SetCurrentValue(input$, true) endif End Sub Sub AssignEventInputToUserVariable(bsp As Object, input$ As String) if type(m.variableToAssign) = "roAssociativeArray" then m.variableToAssign.SetCurrentValue(input$, true) else userVariablesUpdated = false regex = CreateObject("roRegEx", "!!", "i") variableAssignments = regex.Split(input$) if variableAssignments.Count() > 0 then for each variableAssignment in variableAssignments regex = CreateObject("roRegEx",":","i") parts = regex.Split(variableAssignment) if parts.Count() = 2 then variableToAssign$ = parts[0] newValue$ = parts[1] variableToAssign = bsp.GetUserVariable(variableToAssign$) if variableToAssign = invalid then bsp.diagnostics.PrintDebug("User variable " + variableToAssign$ + " not found.") else variableToAssign.SetCurrentValue(newValue$, false) userVariablesUpdated = true endif endif next endif if userVariablesUpdated then userVariablesChanged = CreateObject("roAssociativeArray") userVariablesChanged["EventType"] = "USER_VARIABLES_UPDATED" bsp.msgPort.PostMessage(userVariablesChanged) ' Notify controlling devices to refresh bsp.SendUDPNotification("refresh") endif endif End Sub Function MediaItemEventHandler(event As Object, stateData As Object) As Object if type(event) = "roControlDown" and type(m.auxDisconnectEvents) = "roAssociativeArray" and stri(event.GetSourceIdentity()) = stri(m.bsp.controlPort.GetIdentity()) then m.bsp.diagnostics.PrintDebug("Control Down" + str(event.GetInt())) gpioNum$ = StripLeadingSpaces(str(event.GetInt())) if gpioNum$ = "31" then if type(m.auxDisconnectEvents["BrightSignAuxIn"]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxDisconnect", gpioNum$, "1") return m.ExecuteTransition(m.auxDisconnectEvents["BrightSignAuxIn"], stateData, "") endif endif ' need to update if aux in HW reintroduced ' if type(m.auxConnectEvents) = "roAssociativeArray" then ' if gpioNum$ = "3" then ' if type(m.auxConnectEvents["Aux300Audio1"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxConnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxConnectEvents["Aux300Audio1"], stateData, "") ' endif ' else if gpioNum$ = "4" then ' if type(m.auxConnectEvents["Aux300Audio2"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxConnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxConnectEvents["Aux300Audio2"], stateData, "") ' endif ' else if gpioNum$ = "5" then ' if type(m.auxConnectEvents["Aux300Audio3"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxConnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxConnectEvents["Aux300Audio3"], stateData, "") ' endif ' endif ' endif else if type(event) = "roControlUp" and type(m.auxConnectEvents) = "roAssociativeArray" and stri(event.GetSourceIdentity()) = stri(m.bsp.controlPort.GetIdentity()) then m.bsp.diagnostics.PrintDebug("Control Up" + str(event.GetInt())) gpioNum$ = StripLeadingSpaces(str(event.GetInt())) if gpioNum$ = "31" then if type(m.auxConnectEvents["BrightSignAuxIn"]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxConnect", gpioNum$, "1") return m.ExecuteTransition(m.auxConnectEvents["BrightSignAuxIn"], stateData, "") endif endif ' need to update if aux in HW reintroduced ' if type(m.auxDisconnectEvents) = "roAssociativeArray" then ' if gpioNum$ = "3" then ' if type(m.auxDisconnectEvents["Aux300Audio1"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxDisconnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxDisconnectEvents["Aux300Audio1"], stateData, "") ' endif ' else if gpioNum$ = "4" then ' if type(m.auxDisconnectEvents["Aux300Audio2"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxDisconnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxDisconnectEvents["Aux300Audio2"], stateData, "") ' endif ' else if gpioNum$ = "5" then ' if type(m.auxDisconnectEvents["Aux300Audio3"]) = "roAssociativeArray" then ' m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "auxDisconnect", gpioNum$, "1") ' return m.ExecuteTransition(m.auxDisconnectEvents["Aux300Audio3"], stateData, "") ' endif ' endif ' endif else if type(event) = "roTimerEvent" then if type(m.mstimeoutEvent) = "roAssociativeArray" then if type(m.mstimeoutTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.mstimeoutTimer.GetIdentity()) then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timer", "", "1") return m.ExecuteTransition(m.mstimeoutEvent, stateData, "") endif endif endif if type(m.timeClockEvents) = "roArray" then for each timeClockEvent in m.timeClockEvents if type(timeClockEvent.timer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(timeClockEvent.timer.GetIdentity()) then systemTime = CreateObject("roSystemTime") currentDateTime = systemTime.GetLocalDateTime() ' daily timer if type(timeClockEvent.timeClockDaily%) = "roInt" then triggerEvent = EventActiveToday(currentDateTime, timeClockEvent.daysOfWeek%) ' restart timer LaunchTimeClockEventTimer(m, timeClockEvent) if not triggerEvent then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeClock", "", "0") return "HANDLED" endif ' periodic timer else if type(timeClockEvent.timeClockPeriodicInterval%) = "roInt" then ' units in seconds rather than minutes? currentTime% = currentDateTime.GetHour() * 60 + currentDateTime.GetMinute() startTime% = timeClockEvent.timeClockPeriodicStartTime% endTime% = timeClockEvent.timeClockPeriodicEndTime% intervalTime% = timeClockEvent.timeClockPeriodicInterval% triggerEvent = false withinWindow = TimeWithinWindow(currentTime%, startTime%, endTime%) if withinWindow then triggerEvent = EventActiveToday(currentDateTime, timeClockEvent.daysOfWeek%) endif ' restart timer LaunchTimeClockEventTimer(m, timeClockEvent) if not triggerEvent then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeClock", "", "0") return "HANDLED" endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeClock", "", "1") return m.ExecuteTransition(timeClockEvent.transition, stateData, "") endif endif next endif if type(m.bsp.mediaListInactivity) = "roAssociativeArray" then if type(m.bsp.mediaListInactivity.timer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.bsp.mediaListInactivity.timer.GetIdentity()) then ' reset indices for all media lists if type(m.bsp.mediaListInactivity.mediaListStates) = "roList" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaListInactivityTimer", "", "1") for each mediaListState in m.bsp.mediaListInactivity.mediaListStates mediaListState.playbackIndex% = 0 next else m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaListInactivityTimer", "", "0") endif return "HANDLED" endif endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timer", "", "0") else if type(event) = "roTouchEvent" then touchIndex$ = str(event.GetInt()) m.bsp.diagnostics.PrintDebug("Touch event" + touchIndex$) if type(m.touchEvents) = "roAssociativeArray" then touchEvent = m.touchEvents[touchIndex$] if type(touchEvent) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "touch", touchIndex$, "1") return m.ExecuteTransition(touchEvent, stateData, "") endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "touch", touchIndex$, "0") else if type(event) = "roStreamLineEvent" then serialEvent$ = event.GetString() port$ = event.GetUserData() port% = int(val(port$)) if m.bsp.gpsConfigured and m.bsp.sign.serialPortConfigurations[port%].gps then gpsData = ParseGPSdataGPRMCformat(event) if gpsData.valid then ' log GPS events on first event, then no more frequently than every 30 seconds logGPSEvent = false currentTime = m.bsp.systemTime.GetLocalDateTime() if type(m.nextTimeToLogGPSEvent$) = "roString" then if currentTime.GetString() > m.nextTimeToLogGPSEvent$ then logGPSEvent = true endif else logGPSEvent = true endif if logGPSEvent then currentTime.AddSeconds(30) m.nextTimeToLogGPSEvent$ = currentTime.GetString() endif if gpsData.fixActive then if logGPSEvent then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_GPS_LOCATION, str(gpsData.latitude) + ":" + str(gpsData.longitude)) endif m.bsp.diagnostics.PrintDebug("GPS location: " + str(gpsData.latitude) + "," + str(gpsData.longitude)) m.bsp.gpsLocation.latitude = gpsData.latitude m.bsp.gpsLocation.longitude = gpsData.longitude latitudeInRadians = ConvertDecimalDegtoRad(m.bsp.gpsLocation.latitude) longitudeInRadians = ConvertDecimalDegtoRad(m.bsp.gpsLocation.longitude) for each transition in m.gpsEnterRegionEvents distance = CalcGPSDistance (latitudeInRadians, longitudeInRadians, transition.latitudeInRadians, transition.longitudeInRadians) m.bsp.diagnostics.PrintDebug("GPS distance from longitude " + str(transition.longitude) + ", latitude " + str(transition.latitude) + " = " + str(distance)) if distance < transition.radiusInFeet then m.bsp.diagnostics.PrintDebug("GPS enter region") m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpsEnterRegion", str(gpsData.latitude) + ":" + str(gpsData.longitude), "1") return m.ExecuteTransition(transition, stateData, "") endif next for each transition in m.gpsExitRegionEvents distance = CalcGPSDistance (latitudeInRadians, longitudeInRadians, transition.latitudeInRadians, transition.longitudeInRadians) m.bsp.diagnostics.PrintDebug("GPS distance from longitude " + str(transition.longitude) + ", latitude " + str(transition.latitude) + " = " + str(distance)) if distance > transition.radiusInFeet then m.bsp.diagnostics.PrintDebug("GPS exit region") m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpsExitRegion", str(gpsData.latitude) + ":" + str(gpsData.longitude), "1") return m.ExecuteTransition(transition, stateData, "") endif next else if logGPSEvent then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_GPS_NOT_LOCKED, "") endif m.bsp.gpsLocation.latitude = invalid m.bsp.gpsLocation.longitude = invalid endif else ' print "GPS not valid" endif stateData.nextState = m.superState return "SUPER" endif m.bsp.diagnostics.PrintDebug("Serial Line Event " + event.GetString()) serialEvents = m.serialEvents if type(serialEvents) = "roAssociativeArray" then if type(serialEvents[port$]) = "roAssociativeArray" then ' look for an exact match first if type(serialEvents[port$][serialEvent$]) = "roAssociativeArray" then transition = serialEvents[port$][serialEvent$] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return m.ExecuteTransition(transition, stateData, serialEvent$) else ' if there's no exact match, check for old style wildcard if type(serialEvents[port$]["<*>"]) = "roAssociativeArray" then serialEvent$ = "<*>" transition = serialEvents[port$][serialEvent$] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + event.GetString(), "1") if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, event.GetString()) endif return m.ExecuteTransition(transition, stateData, event.GetString()) else ' look for regular expression match with each of the possible serial events for the current state for each serialEventSpec in serialEvents[port$] ' only look for regular expressions if spec includes wildcard if instr(1, serialEventSpec, "(.*)") > 0 then r = CreateObject("roRegEx", serialEventSpec, "i") if type(r) = "roRegex" then matches = r.match(serialEvent$) if matches.Count() > 0 then transition = serialEvents[port$][serialEventSpec] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, event.GetString()) endif if matches.Count() > 1 and transition.assignWildcardToUserVariable then transition.AssignWildcardInputToUserVariable(m.bsp, matches[1]) endif return m.ExecuteTransition(transition, stateData, serialEvent$) endif endif endif next endif endif endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "0") else if type(event) = "roStreamByteEvent" then m.bsp.diagnostics.PrintDebug("Serial Byte Event " + str(event.GetInt())) serialByte% = event.GetInt() port$ = event.GetUserData() ' compare the serialStreamInput to all expected inputs. execute transition if a match is found. if type(m.serialEvents[port$]) = "roAssociativeArray" then port% = int(val(port$)) serialStreamInput = m.stateMachine.serialStreamInputBuffers[port%] while serialStreamInput.Count() >= 64 serialStreamInput.Shift() end while serialStreamInput.push(serialByte%) if type(m.serialEvents[port$].streamInputTransitionSpecs) = "roArray" then streamInputTransitionSpecs = m.serialEvents[port$].streamInputTransitionSpecs for i% = 0 to streamInputTransitionSpecs.Count() - 1 streamInputTransitionSpec = streamInputTransitionSpecs[i%] streamInputSpec = streamInputTransitionSpec.inputSpec if ByteArraysMatch(serialStreamInput, streamInputSpec) then serialStreamInput.Clear() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serialBytes", port$ + " " + streamInputTransitionSpec.asciiSpec, "1") return m.ExecuteTransition(streamInputTransitionSpec.transition, stateData, "") endif next endif endif else if type(event) = "roUsbBinaryEtapEvent" then m.bsp.diagnostics.PrintDebug("UsbBinaryEtap Event " + event.GetByteArray().ToHexString()) ba = event.GetByteArray() ' compare the byte array received to all expected inputs. execute transition if a match is found. if type(m.usbBinaryEtapEvents) = "roArray" then for each usbBinaryEtapInputTransitionSpec in m.usbBinaryEtapEvents if ByteArraysMatch(usbBinaryEtapInputTransitionSpec.inputSpec, ba) then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "usbBinaryEtap", usbBinaryEtapInputTransitionSpec.asciiSpec, "1") return m.ExecuteTransition(usbBinaryEtapInputTransitionSpec.transition, stateData, "") endif next endif else if type(event) = "roIRRemotePress" then m.bsp.diagnostics.PrintDebug("Remote Event" + stri(event.GetInt())) remoteEvent% = event.GetInt() remoteEvent$ = ConvertToRemoteCommand(remoteEvent%) remoteEvents = m.remoteEvents if type(remoteEvents) = "roAssociativeArray" then if type(remoteEvents[remoteEvent$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "remote", remoteEvent$, "1") return m.ExecuteTransition(m.remoteEvents[remoteEvent$], stateData, remoteEvent$) endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "remote", remoteEvent$, "0") else if type(event) = "roKeyboardPress" then ' note - this code does not fully cover the case where one state is waiting for keyboard input ' and another state is waiting for barcode input. m.bsp.diagnostics.PrintDebug("Keyboard Press" + str(event.GetInt())) keyboardChar$ = chr(event.GetInt()) keyboardCharForLog$ = StripLeadingSpaces(stri(event.GetInt())) usbStringEvents = m.usbStringEvents keyboardEvents = m.keyboardEvents checkUSBInputString = false if type(usbStringEvents) = "roAssociativeArray" then if event.GetInt() <> 13 then m.usbInputBuffer$ = m.usbInputBuffer$ + keyboardChar$ if m.usbInputLogBuffer$ = "" then m.usbInputLogBuffer$ = keyboardCharForLog$ else m.usbInputLogBuffer$ = m.usbInputLogBuffer$ + "," + keyboardCharForLog$ endif checkUSBInputString = false else checkUSBInputString = true endif endif ' check for bar code input (usb characters terminated by an Enter key) if type(usbStringEvents) = "roAssociativeArray" then if checkUSBInputString then if type(usbStringEvents[m.usbInputBuffer$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "usb", m.usbInputLogBuffer$, "1") action$ = m.ExecuteTransition(m.usbStringEvents[m.usbInputBuffer$], stateData, m.usbInputBuffer$) if event.GetInt() = 13 then m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" endif return action$ else ' check for wildcards usbInputBuffer$ = "" if type(usbStringEvents[usbInputBuffer$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "usb", m.usbInputLogBuffer$, "1") action$ = m.ExecuteTransition(m.usbStringEvents[usbInputBuffer$], stateData, m.usbInputBuffer$) if event.GetInt() = 13 then m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" endif return action$ endif endif endif endif ' check for single keyboard characters keyboardPayload$ = keyboardChar$ if type(keyboardEvents) = "roAssociativeArray" then ' if keyboard input is non printable character, convert it to the special code keyboardCode$ = m.bsp.GetNonPrintableKeyboardCode(event.GetInt()) if keyboardCode$ <> "" then keyboardChar$ = keyboardCode$ keyboardPayload$ = keyboardChar$ endif if type(keyboardEvents[keyboardChar$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "keyboard", keyboardCharForLog$, "1") action$ = m.ExecuteTransition(m.keyboardEvents[keyboardChar$], stateData, keyboardPayload$) if event.GetInt() = 13 then m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" endif return action$ else if type(keyboardEvents[""]) = "roAssociativeArray" then keyboardChar$ = "" m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "keyboard", keyboardCharForLog$, "1") action$ = m.ExecuteTransition(m.keyboardEvents[keyboardChar$], stateData, keyboardPayload$) if event.GetInt() = 13 then m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" endif return action$ endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "usb", keyboardCharForLog$, "0") ' clear the buffer when the user presses enter if event.GetInt() = 13 then m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" endif else if type(event) = "roSyncManagerEvent" then synchronizeEvent$ = event.GetId() synchronizeEvents = m.synchronizeEvents if type(synchronizeEvents) = "roAssociativeArray" then if type(synchronizeEvents[synchronizeEvent$]) = "roAssociativeArray" or type(synchronizeEvents[""]) = "roAssociativeArray" then m.stateMachine.syncInfo = CreateObject("roAssociativeArray") m.stateMachine.syncInfo.SyncDomain = event.GetDomain() m.stateMachine.syncInfo.SyncId = event.GetId() m.stateMachine.syncInfo.SyncIsoTimestamp = event.GetIsoTimestamp() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "enhancedSynchronize", synchronizeEvent$, "1") if type(synchronizeEvents[synchronizeEvent$]) = "roAssociativeArray" return m.ExecuteTransition(m.synchronizeEvents[synchronizeEvent$], stateData, synchronizeEvent$) else ' Synchronising with a PlayFile state return m.ExecuteTransition(m.synchronizeEvents[""], stateData, synchronizeEvent$) endif endif endif else if type(event) = "roDatagramEvent" then ' could be either a udp event or a synchronize event m.bsp.diagnostics.PrintDebug("UDP Event " + event.GetString()) udpEvent$ = event.GetString() synchronizeEvents = m.synchronizeEvents udpEvents = m.udpEvents ' check to see if this is a synchronization preload or play event if type(synchronizeEvents) = "roAssociativeArray" then index% = instr(1, udpEvent$, "pre-") if index% = 1 then ' preload next file synchronizeEvent$ = mid(udpEvent$, 5) if type(synchronizeEvents[synchronizeEvent$]) = "roAssociativeArray" then ' get the next file and preload it nextState$ = synchronizeEvents[synchronizeEvent$].targetMediaState$ nextState = m.stateMachine.stateTable[nextState$] preloadRequired = true if type(m.stateMachine.preloadState) = "roAssociativeArray" then if m.stateMachine.preloadedStateName$ = nextState.name$ preloadRequired = false endif endif ' set this variable so that launchVideo knows what has been preloaded m.stateMachine.preloadState = nextState ' currently only support preload / synchronizing with images and videos if preloadRequired then m.stateMachine.preloadState.PreloadItem() endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "synchronize-pre", synchronizeEvent$, "1") ' ?? return "HANDLED" ?? endif endif index% = instr(1, udpEvent$, "ply-") if index% = 1 then ' just transition to the next state where the file will be played synchronizeEvent$ = mid(udpEvent$, 5) if type(synchronizeEvents[synchronizeEvent$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "synchronize-play", synchronizeEvent$, "1") return m.ExecuteTransition(m.synchronizeEvents[synchronizeEvent$], stateData, "") endif endif endif if type(udpEvents) = "roAssociativeArray" then if type(udpEvents[udpEvent$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", udpEvent$, "1") transition = udpEvents[udpEvent$] return m.ExecuteTransition(transition, stateData, udpEvent$) else ' if there's no exact match, check for old style wildcard if type(udpEvents[""]) = "roAssociativeArray" then udpEvent$ = "" m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", event.GetString(), "1") transition = udpEvents[udpEvent$] if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, event.GetString()) endif return m.ExecuteTransition(transition, stateData, event.GetString()) else ' look for regular expression match with each of the possible udp events for the current state for each udpEventSpec in udpEvents ' only look for regular expressions if spec includes wildcard if instr(1, udpEventSpec, "(.*)") > 0 then r = CreateObject("roRegEx", udpEventSpec, "i") if type(r) = "roRegex" then matches = r.match(udpEvent$) if matches.Count() > 0 then transition = udpEvents[udpEventSpec] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", udpEvent$, "1") if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, event.GetString()) endif if matches.Count() > 1 and transition.assignWildcardToUserVariable then transition.AssignWildcardInputToUserVariable(m.bsp, matches[1]) endif return m.ExecuteTransition(transition, stateData, udpEvent$) endif endif endif next endif endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", event.GetString(), "0") else if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "BPControlDown" then bpIndex$ = event["ButtonPanelIndex"] bpIndex% = int(val(bpIndex$)) bpNum$ = event["ButtonNumber"] bpNum% = int(val(bpNum$)) m.bsp.diagnostics.PrintDebug("BP Press" + bpNum$ + " on button panel" + bpIndex$) bpEvents = m.bpEvents ' bpEvents["-1"] => any bp button currentBPEvent = bpEvents[bpIndex%] transition = currentBPEvent[bpNum$] if type(transition) <> "roAssociativeArray" then transition = currentBPEvent["-1"] endif if type(transition) = "roAssociativeArray" then payload$ = bpIndex$ + "-" + StripLeadingSpaces(stri(bpNum% + 1)) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return m.ExecuteTransition(transition, stateData, payload$) endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "0") else if event["EventType"] = "GPIOControlDown" then gpioNum$ = event["ButtonNumber"] m.bsp.diagnostics.PrintDebug("Control Down " + gpioNum$) gpioEvents = m.gpioEvents if type(gpioEvents[gpioNum$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return m.ExecuteTransition(gpioEvents[gpioNum$], stateData, "") endif else if event["EventType"] = "GPIOControlUp" then gpioNum$ = event["ButtonNumber"] m.bsp.diagnostics.PrintDebug("Control Up " + gpioNum$) gpioUpEvents = m.gpioUpEvents if type(gpioUpEvents[gpioNum$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return m.ExecuteTransition(gpioUpEvents[gpioNum$], stateData, "") endif else if event["EventType"] = "SEND_ZONE_MESSAGE" then sendZoneMessageParameter$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("ZoneMessageEvent " + sendZoneMessageParameter$) if type(m.zoneMessageEvents) = "roAssociativeArray" then if type(m.zoneMessageEvents[sendZoneMessageParameter$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendZoneMessage", sendZoneMessageParameter$, "1") return m.ExecuteTransition(m.zoneMessageEvents[sendZoneMessageParameter$], stateData, "") endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendZoneMessage", sendZoneMessageParameter$, "0") else if event["EventType"] = "EVENT_PLUGIN_MESSAGE" then pluginName$ = event["PluginName"] pluginMessage$ = event["PluginMessage"] m.bsp.diagnostics.PrintDebug("PluginMessageEvent " + pluginName$ + " " + pluginMessage$) key$ = pluginName$ + pluginMessage$ if type(m.pluginMessageEvents) = "roAssociativeArray" then if type(m.pluginMessageEvents[key$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendPluginMessage", key$, "1") return m.ExecuteTransition(m.pluginMessageEvents[key$], stateData, "") else key$ = pluginName$ + "" if type(m.pluginMessageEvents[key$]) = "roAssociativeArray" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendPluginMessage", key$, "1") return m.ExecuteTransition(m.pluginMessageEvents[key$], stateData, "") endif endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendPluginMessage", key$, "0") else if event["EventType"] = "INTERNAL_SYNC_PRELOAD" then internalSyncParameter$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("InternalSyncPreloadEvent " + internalSyncParameter$) actedOn$ = "0" if type(m.internalSynchronizeEvents) = "roAssociativeArray" then if type(m.internalSynchronizeEvents[internalSyncParameter$]) = "roAssociativeArray" then nextState$ = m.internalSynchronizeEvents[internalSyncParameter$].targetMediaState$ m.stateMachine.preloadState = m.stateMachine.stateTable[nextState$] m.stateMachine.preloadState.PreloadItem() actedOn$ = "1" endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "internalSyncPreload", internalSyncParameter$, actedOn$) return "HANDLED" else if event["EventType"] = "INTERNAL_SYNC_MASTER_PRELOAD" then internalSyncParameter$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("InternalSyncMasterPreload " + internalSyncParameter$) actedOn$ = "0" if type(m.internalSynchronizeEventsMaster) = "roAssociativeArray" then if type(m.internalSynchronizeEventsMaster[internalSyncParameter$]) = "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("post play message with parameter " + internalSyncParameter$) internalSyncPlay = CreateObject("roAssociativeArray") internalSyncPlay["EventType"] = "INTERNAL_SYNC_PLAY" internalSyncPlay["EventParameter"] = internalSyncParameter$ m.stateMachine.msgPort.PostMessage(internalSyncPlay) actedOn$ = "1" endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "internalSyncMasterPreload", internalSyncParameter$, actedOn$) return "HANDLED" else if event["EventType"] = "INTERNAL_SYNC_PLAY" then internalSyncParameter$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("InternalSyncPlayEvent " + internalSyncParameter$) if type(m.internalSynchronizeEventsMaster) = "roAssociativeArray" then if type(m.internalSynchronizeEventsMaster[internalSyncParameter$]) = "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("master play event received - prepare to return") m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "internalSyncMasterPlay", internalSyncParameter$, "1") return m.ExecuteTransition(m.internalSynchronizeEventsMaster[internalSyncParameter$], stateData, "") endif endif if type(m.internalSynchronizeEvents) = "roAssociativeArray" then if type(m.internalSynchronizeEvents[internalSyncParameter$]) = "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("slave play event received - prepare to return") m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "internalSyncSlavePlay", internalSyncParameter$, "1") return m.ExecuteTransition(m.internalSynchronizeEvents[internalSyncParameter$], stateData, "") endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "internalSyncPlay", internalSyncParameter$, "0") else if event["EventType"] = "PREPARE_FOR_RESTART" then m.bsp.diagnostics.PrintDebug(m.id$ + " - PREPARE_FOR_RESTART") if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer = invalid endif if IsAudioPlayer(m.stateMachine.audioPlayer) then m.stateMachine.audioPlayer = invalid endif if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer = invalid endif return "HANDLED" endif endif else if (type(event) = "roVideoEvent" and type(m.stateMachine.videoPlayer) = "roVideoPlayer" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity()) or (type(event) = "roAudioEvent" and IsAudioPlayer(m.stateMachine.audioPlayer) and event.GetSourceIdentity() = m.stateMachine.audioPlayer.GetIdentity()) then if event.GetInt() = 8 then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "0") else if event.GetInt() = 12 then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "videoTimeCode", "", "0") endif endif stateData.nextState = m.superState return "SUPER" End Function ' m is bsp Sub WaitForSyncResponse(parameter$ As String) udpReceiver = CreateObject("roDatagramReceiver", m.udpReceivePort) msgPort = CreateObject("roMessagePort") udpReceiver.SetPort(msgPort) m.udpSender.Send("ply-" + parameter$) while true msg = wait(50, msgPort) if type(msg) = "roDatagramEvent" or msg = invalid then udpReceiver = invalid return endif endwhile End Sub Function EventActiveToday(currentDateTime As Object, daysOfWeek% As Integer) As Boolean bitwiseDaysOfWeek% = daysOfWeek% currentDayOfWeek = currentDateTime.GetDayOfWeek() bitDayOfWeek% = 2 ^ currentDayOfWeek if (bitwiseDaysOfWeek% and bitDayOfWeek%) <> 0 then return true endif return false End Function Function TimeWithinWindow(currentTime% As Integer, startTime% As Integer, endTime% As Integer) As Boolean withinWindow = false if startTime% = endTime% then withinWindow = true else if startTime% < endTime% then if currentTime% >= startTime% and currentTime% < endTime% then withinWindow = true endif else if currentTime% < endTime% or currentTime% > startTime% then withinWindow = true endif return withinWindow End Function Function IsTimeoutInFuture(timeoutDateTime As Object) As Boolean systemTime = CreateObject("roSystemTime") currentDateTime = systemTime.GetLocalDateTime() systemTime = invalid return currentDateTime.GetString() < timeoutDateTime.GetString() End Function Sub LaunchTimeClockEventTimer(state As Object, timeClockEvent As Object) if type(timeClockEvent.timer) = "roTimer" then timeClockEvent.timer.Stop() timeClockEvent.timer = invalid endif timer = CreateObject("roTimer") if type(timeClockEvent.timeClockEventDateTime) = "roDateTime" then dateTime = timeClockEvent.timeClockEventDateTime ' only set timer if it is in the future if not IsTimeoutInFuture(dateTime) then return endif state.bsp.diagnostics.PrintDebug("Set timeout to " + dateTime.GetString()) timer.SetDateTime(dateTime) else if type(timeClockEvent.userVariableName$) = "roString" then if type(timeClockEvent.userVariable) = "roAssociativeArray" then dateTime$ = timeClockEvent.userVariable.GetCurrentValue() dateTime = FixDateTime(dateTime$) if type(dateTime) = "roDateTime" then ' only set timer if it is in the future if not IsTimeoutInFuture(dateTime) then print "Specified timer is in the past, don't set it: timer time is ";dateTime.GetString() return endif state.bsp.diagnostics.PrintDebug("Set timeout to " + dateTime.GetString()) timer.SetDateTime(dateTime) else state.bsp.diagnostics.PrintDebug("Timeout specification " + dateTime$ + " is invalid") state.bsp.logging.WriteDiagnosticLogEntry(state.bsp.diagnosticCodes.EVENT_INVALID_DATE_TIME_SPEC, dateTime$) endif endif else if type(timeClockEvent.timeClockDaily%) = "roInt" then hours% = timeClockEvent.timeClockDaily% / 60 minutes% = timeClockEvent.timeClockDaily% - (hours% * 60) timer.SetTime(hours%, minutes%, 0, 0) timer.SetDate(-1, -1, -1) else systemTime = CreateObject("roSystemTime") currentDateTime = systemTime.GetLocalDateTime() ' units in seconds rather than minutes? currentTime% = currentDateTime.GetHour() * 60 + currentDateTime.GetMinute() startTime% = timeClockEvent.timeClockPeriodicStartTime% endTime% = timeClockEvent.timeClockPeriodicEndTime% intervalTime% = timeClockEvent.timeClockPeriodicInterval% withinWindow = TimeWithinWindow(currentTime%, startTime%, endTime%) ' print "currentDateTime = ";currentDateTime.GetString() ' print "currentTime% = ";currentTime% ' print "withinWindow = ";withinWindow if not withinWindow then ' set timer for next start time hours% = startTime% / 60 minutes% = startTime% - (hours% * 60) timer.SetTime(hours%, minutes%, 0, 0) timer.SetDate(-1, -1, -1) ' print "set time to ";hours%;" hours, ";minutes%;" minutes" else ' set timer for next appropriate time if currentTime% > startTime% then minutesSinceStartTime% = currentTime% - startTime% else minutesSinceStartTime% = currentTime% + (24 * 60 - startTime%) endif ' elapsed intervals since the start time? numberOfElapsedIntervals% = minutesSinceStartTime% / intervalTime% numberOfIntervalsUntilNextTimeout% = numberOfElapsedIntervals% + 1 ' determine time for next timeout nextTimeoutTime% = startTime% + (numberOfIntervalsUntilNextTimeout% * intervalTime%) ' check for wrap to next day if nextTimeoutTime% > (24 * 60) then nextTimeoutTime% = nextTimeoutTime% - (24 * 60) endif ' set timer for next start time hours% = nextTimeoutTime% / 60 minutes% = nextTimeoutTime% - (hours% * 60) timer.SetTime(hours%, minutes%, 0, 0) timer.SetDate(-1, -1, -1) state.bsp.diagnostics.PrintDebug("Set timeout to " + stri(hours%) + " hours, " + stri(minutes%) + " minutes.") endif systemTime = invalid endif timer.SetPort(state.stateMachine.msgPort) timer.Start() timeClockEvent.timer = timer End Sub Sub LaunchTimer() if type(m.mstimeoutEvent) = "roAssociativeArray" then timer = CreateObject("roTimer") timer.SetPort(m.stateMachine.msgPort) timer.SetElapsed(0, m.mstimeoutValue%) timer.Start() m.mstimeoutTimer = timer endif if type(m.timeClockEvents) = "roArray" then for each timeClockEvent in m.timeClockEvents LaunchTimeClockEventTimer(m, timeClockEvent) next endif End Sub Sub PreloadItem() zone = m.stateMachine if m.type$ = "mediaList" or m.type$ = "mediaRSS" or m.type$ = "signChannel" or m.type$ = "liveText" or m.type$ = "interactiveMenuItem" then zone.preloadedStateName$ = "" return endif if m.type$ = "playFile" then fileTableEntry = m.filesTable.Lookup(m.payload$) fileName$ = fileTableEntry.fileName$ fileType$ = fileTableEntry.fileType$ if fileType$ = "image" then imageItem = {} imageItem.fileName$ = fileName$ imageItem.useImageBuffer = false else if fileType$ = "video" then videoItem = {} videoItem.fileName$ = fileName$ videoItem.probeData = fileTableEntry.probeData endif else if type(m.imageItem) = "roAssociativeArray" then imageItem = {} imageItem.fileName$ = m.imageItem.fileName$ imageItem.useImageBuffer = m.imageItem.useImageBuffer else if type(m.videoItem) = "roAssociativeArray" then videoItem = {} videoItem.fileName$ = m.videoItem.fileName$ if type(m.videoItem.probeData) = "roString" then videoItem.probeData = m.videoItem.probeData else videoItem.probeData = invalid endif endif if type(imageItem) = "roAssociativeArray" then if imageItem.useImageBuffer = true then m.bsp.diagnostics.PrintDebug("Did not preload file in PreloadItem as it is using an image buffer: " + imageItem.fileName$) else imageItemFilePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, imageItem.fileName$) zone.imagePlayer.PreloadFile(imageItemFilePath$) zone.preloadedStateName$ = m.name$ m.bsp.diagnostics.PrintDebug("Preloaded file in PreloadItem: " + imageItem.fileName$ + ", " + imageItemFilePath$) endif else if type(videoItem) = "roAssociativeArray" then videoItemFilePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, videoItem.fileName$) aa = { } aa.AddReplace("Filename", videoItemFilePath$) if type(videoItem.probeData) = "roString" then m.bsp.diagnostics.PrintDebug("PreloadItem: probeData = " + videoItem.probeData) aa.AddReplace("ProbeString", videoItem.probeData) endif ok = zone.videoPlayer.PreloadFile(aa) zone.preloadedStateName$ = m.name$ m.bsp.diagnostics.PrintDebug("Preloaded file in PreloadItem: " + videoItem.fileName$) endif End Sub 'endregion 'region Images State Machine ' ************************************************* ' ' Images State Machine ' ' ************************************************* Function newImagesZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = ImageZoneConstructor zoneHSM.InitialPseudostateHandler = ImageZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) zoneHSM.imageMode% = GetImageModeValue(zoneXML.zoneSpecificParameters.imageMode.GetText()) zoneHSM.numImageItems% = 0 return zoneHSM End Function Sub ImageZoneConstructor() m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m if zoneHSM.numImageItems% > 0 then imagePlayer = CreateObject("roImageWidget", zoneHSM.rectangle) zoneHSM.imagePlayer = imagePlayer ' initialize image player parameters imagePlayer.SetDefaultMode(zoneHSM.imageMode%) m.LoadImageBuffers() else zoneHSM.imagePlayer = invalid endif m.CreateObjects() m.activeState = m.playlist.firstState if type(m.playlist.firstState) = "roAssociativeArray" then m.previousStateName$ = m.playlist.firstState.id$ else m.previousStateName$ = "" endif End Sub Function ImageZoneGetInitialState() As Object return m.activeState End Function Function STDisplayingImageEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.DisplayImage("image") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function 'endregion 'region Enhanced Audio State Machine ' ************************************************* ' ' Enhanced Audio State Machine ' ' ************************************************* Function newEnhancedAudioZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = EnhancedAudioZoneConstructor zoneHSM.InitializeAudioZoneCommon = InitializeAudioZoneCommon zoneHSM.InitialPseudostateHandler = EnhancedAudioZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) newAudioZoneCommon(zoneXML, zoneHSM) zoneHSM.fadeLength% = 4 fadeLength$ = zoneXML.zoneSpecificParameters.fadeLength.GetText() if fadeLength$ <> "" then zoneHSM.fadeLength% = int(val(fadeLength$)) endif return zoneHSM End Function Sub EnhancedAudioZoneConstructor() audioPlayer = CreateObject("roAudioPlayerMx") m.InitializeAudioZoneCommon(audioPlayer) End Sub Function EnhancedAudioZoneGetInitialState() As Object return m.activeState End Function 'endregion 'region Audio State Machine ' ************************************************* ' ' Audio State Machine ' ' ************************************************* Function newAudioZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = AudioZoneConstructor zoneHSM.InitializeAudioZoneCommon = InitializeAudioZoneCommon zoneHSM.InitialPseudostateHandler = AudioZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) newAudioZoneCommon(zoneXML, zoneHSM) return zoneHSM End Function Sub newAudioZoneCommon(zoneXML As Object, zoneHSM As Object) zoneHSM.audioOutput% = GetAudioOutputValue(zoneXML.zoneSpecificParameters.audioOutput.GetText()) zoneHSM.audioMode% = GetAudioModeValue(zoneXML.zoneSpecificParameters.audioMode.GetText()) zoneHSM.audioMapping% = GetAudioMappingValue(zoneXML.zoneSpecificParameters.audioMapping.GetText()) zoneHSM.audioMappingSpan% = GetAudioMappingSpan(zoneHSM.audioOutput%, zoneXML.zoneSpecificParameters.audioMapping.GetText()) zoneHSM.analogOutput$ = zoneXML.zoneSpecificParameters.analogOutput.GetText() zoneHSM.analog2Output$ = zoneXML.zoneSpecificParameters.analog2Output.GetText() zoneHSM.analog3Output$ = zoneXML.zoneSpecificParameters.analog3Output.GetText() zoneHSM.hdmiOutput$ = zoneXML.zoneSpecificParameters.hdmiOutput.GetText() zoneHSM.spdifOutput$ = zoneXML.zoneSpecificParameters.spdifOutput.GetText() zoneHSM.usbOutput$ = zoneXML.zoneSpecificParameters.usbOutput.GetText() zoneHSM.audioMixMode$ = zoneXML.zoneSpecificParameters.audioMixMode.GetText() if zoneHSM.analogOutput$ <> "" and zoneHSM.hdmiOutput$ <> "" and zoneHSM.spdifOutput$ <> "" and zoneHSM.audioMixMode$ <> "" then zoneHSM.presentationUsesRoAudioOutputParameters = true else zoneHSM.presentationUsesRoAudioOutputParameters = false endif zoneHSM.initialAudioVolume% = 100 audioVolume$ = zoneXML.zoneSpecificParameters.audioVolume.GetText() if audioVolume$ <> "" then zoneHSM.initialAudioVolume% = int(val(audioVolume$)) endif zoneHSM.minimumVolume% = 0 minimumVolume$ = zoneXML.zoneSpecificParameters.minimumVolume.GetText() if minimumVolume$ <> "" then zoneHSM.minimumVolume% = int(val(minimumVolume$)) endif zoneHSM.maximumVolume% = 0 maximumVolume$ = zoneXML.zoneSpecificParameters.maximumVolume.GetText() if maximumVolume$ <> "" then zoneHSM.maximumVolume% = int(val(maximumVolume$)) endif End Sub Sub InitializeAudioZoneCommon(audioPlayer As Object) m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m ' create players zoneHSM.audioVolume% = zoneHSM.initialAudioVolume% zoneHSM.audioChannelVolumes = CreateObject("roArray", 6, true) for i% = 0 to 5 zoneHSM.audioChannelVolumes[i%] = zoneHSM.audioVolume% next ' initialize audio player parameters audioPlayer.SetPort(zoneHSM.msgPort) zoneHSM.audioPlayer = audioPlayer m.SetAudioOutputAndMode(audioPlayer) ' audioPlayer.SetAudioOutput(zoneHSM.audioOutput%) ' audioPlayer.SetAudioMode(zoneHSM.audioMode%) audioPlayer.MapStereoOutput(zoneHSM.audioMapping%) audioPlayer.SetStereoMappingSpan(zoneHSM.audioMappingSpan%) audioPlayer.SetVolume(zoneHSM.audioVolume%) audioPlayer.SetLoopMode(false) ' Panther only zoneHSM.ConfigureAudioResources() zoneHSM.audioPlayerAudioSettings = CreateObject("roAssociativeArray") zoneHSM.audioPlayerAudioSettings.audioOutput% = zoneHSM.audioOutput% zoneHSM.audioPlayerAudioSettings.stereoMapping% = zoneHSM.audioMapping% zoneHSM.audioPlayerAudioSettings.audioMappingSpan% = zoneHSM.audioMappingSpan% m.bsp.SetAudioVolumeLimits(zoneHSM, zoneHSM.audioPlayerAudioSettings) m.activeState = m.playlist.firstState if type(m.playlist.firstState) = "roAssociativeArray" then m.previousStateName$ = m.playlist.firstState.id$ else m.previousStateName$ = "" endif m.CreateObjects() End Sub Sub AudioZoneConstructor() audioPlayer = CreateObject("roAudioPlayer") m.InitializeAudioZoneCommon(audioPlayer) End Sub Function AudioZoneGetInitialState() As Object return m.activeState End Function 'endregion 'region Video State Machine ' ************************************************* ' ' Video State Machine ' ' ************************************************* Function newVideoZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.InitializeVideoZoneObjects = InitializeVideoZoneObjects zoneHSM.ConstructorHandler = VideoZoneConstructor zoneHSM.InitialPseudostateHandler = VideoZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) zoneHSM.viewMode% = GetViewModeValue(zoneXML.zoneSpecificParameters.viewMode.GetText()) zoneHSM.audioOutput% = GetAudioOutputValue(zoneXML.zoneSpecificParameters.audioOutput.GetText()) zoneHSM.audioMode% = GetAudioModeValue(zoneXML.zoneSpecificParameters.audioMode.GetText()) zoneHSM.audioMapping% = GetAudioMappingValue(zoneXML.zoneSpecificParameters.audioMapping.GetText()) zoneHSM.audioMappingSpan% = GetAudioMappingSpan(zoneHSM.audioOutput%, zoneXML.zoneSpecificParameters.audioMapping.GetText()) zoneHSM.analogOutput$ = zoneXML.zoneSpecificParameters.analogOutput.GetText() zoneHSM.analog2Output$ = zoneXML.zoneSpecificParameters.analog2Output.GetText() zoneHSM.analog3Output$ = zoneXML.zoneSpecificParameters.analog3Output.GetText() zoneHSM.hdmiOutput$ = zoneXML.zoneSpecificParameters.hdmiOutput.GetText() zoneHSM.spdifOutput$ = zoneXML.zoneSpecificParameters.spdifOutput.GetText() zoneHSM.usbOutput$ = zoneXML.zoneSpecificParameters.usbOutput.GetText() zoneHSM.audioMixMode$ = zoneXML.zoneSpecificParameters.audioMixMode.GetText() if zoneHSM.analogOutput$ <> "" and zoneHSM.hdmiOutput$ <> "" and zoneHSM.spdifOutput$ <> "" and zoneHSM.audioMixMode$ <> "" then zoneHSM.presentationUsesRoAudioOutputParameters = true else zoneHSM.presentationUsesRoAudioOutputParameters = false endif zoneHSM.initialVideoVolume% = 100 videoVolume$ = zoneXML.zoneSpecificParameters.videoVolume.GetText() if videoVolume$ <> "" then zoneHSM.initialVideoVolume% = int(val(videoVolume$)) endif zoneHSM.initialAudioVolume% = 100 audioVolume$ = zoneXML.zoneSpecificParameters.audioVolume.GetText() if audioVolume$ <> "" then zoneHSM.initialAudioVolume% = int(val(audioVolume$)) endif zoneHSM.minimumVolume% = 0 minimumVolume$ = zoneXML.zoneSpecificParameters.minimumVolume.GetText() if minimumVolume$ <> "" then zoneHSM.minimumVolume% = int(val(minimumVolume$)) endif zoneHSM.maximumVolume% = 0 maximumVolume$ = zoneXML.zoneSpecificParameters.maximumVolume.GetText() if maximumVolume$ <> "" then zoneHSM.maximumVolume% = int(val(maximumVolume$)) endif zoneHSM.videoInput$ = zoneXML.zoneSpecificParameters.liveVideoInput.GetText() zoneHSM.videoStandard$ = zoneXML.zoneSpecificParameters.liveVideoStandard.GetText() zoneHSM.brightness% = int(val(zoneXML.zoneSpecificParameters.brightness.GetText())) zoneHSM.contrast% = int(val(zoneXML.zoneSpecificParameters.contrast.GetText())) zoneHSM.saturation% = int(val(zoneXML.zoneSpecificParameters.saturation.GetText())) zoneHSM.hue% = int(val(zoneXML.zoneSpecificParameters.hue.GetText())) zoneHSM.zOrderFront = true zOrderFront$ = zoneXML.zoneSpecificParameters.zOrderFront.GetText() if lcase(zOrderFront$) = "false" then zoneHSM.zOrderFront = false endif return zoneHSM End Function ' use roAudioOutput if all of the following are true ' current device supports roAudioOutput (m.bsp.sysInfo.modelSupportsRoAudioOutput) ' current presentation was published for a device that supports roAudioOutput (m.bsp.currentPresentationUsesRoAudioOutputParameters) ' current presentation includes parameters for roAudioOutput (m.presentationUsesRoAudioOutputParameters) ' this only applies to Panther. Old Panther presentations did not use roAudioOutput parameters Sub SetAudioOutputAndMode(player As Object) if m.bsp.sysInfo.modelSupportsRoAudioOutput and m.presentationUsesRoAudioOutputParameters and m.bsp.currentPresentationUsesRoAudioOutputParameters then pcm = CreateObject("roArray", 1, true) compressed = CreateObject("roArray", 1, true) multichannel = CreateObject("roArray", 1, true) analogAudioOutput = CreateObject("roAudioOutput", "Analog:1") analog2AudioOutput = CreateObject("roAudioOutput", "Analog:2") analog3AudioOutput = CreateObject("roAudioOutput", "Analog:3") hdmiAudioOutput = CreateObject("roAudioOutput", "HDMI") spdifAudioOutput = CreateObject("roAudioOutput", "SPDIF") usbAudioOutput = CreateObject("roAudioOutput", "USB") if lcase(m.analogOutput$) <> "none" and lcase(m.analogOutput$) <> "multichannel" then pcm.push(analogAudioOutput) endif if lcase(m.analog2Output$) = "pcm" then pcm.push(analog2AudioOutput) endif if lcase(m.analog3Output$) = "pcm" then pcm.push(analog3AudioOutput) endif if lcase(m.analogOutput$)="multichannel" then multichannel.push(analogAudioOutput) else if lcase(m.analog2Output$)="multichannel" then multichannel.push(analog2AudioOutput) else if lcase(m.analog3Output$)="multichannel" then multichannel.push(analog3AudioOutput) endif if lcase(m.hdmiOutput$) = "passthrough" then compressed.push(hdmiAudioOutput) else if lcase(m.hdmiOutput$) <> "none" then pcm.push(hdmiAudioOutput) endif if lcase(m.spdifOutput$) = "passthrough" then compressed.push(spdifAudioOutput) else if lcase(m.spdifOutput$) <> "none" then pcm.push(spdifAudioOutput) endif if lcase(m.usbOutput$) = "pcm" then pcm.push(usbAudioOutput) else if lcase(m.usbOutput$) = "multichannel" then multichannel.push(usbAudioOutput) endif if pcm.Count() = 0 then noPCMAudioOutput = CreateObject("roAudioOutput", "none") pcm.push(noPCMAudioOutput) endif if compressed.Count() = 0 then noCompressedAudioOutput = CreateObject("roAudioOutput", "none") compressed.push(noCompressedAudioOutput) endif if multichannel.Count() = 0 then noMultichannelAudioOutput = CreateObject("roAudioOutput", "none") multichannel.push(noMultichannelAudioOutput) endif player.SetPcmAudioOutputs(pcm) player.SetCompressedAudioOutputs(compressed) player.SetMultichannelAudioOutputs(multichannel) if lcase(m.audioMixMode$) = "passthrough" then player.SetAudioMode(0) else if lcase(m.audioMixMode$) = "left" then player.SetAudioMode(3) else if lcase(m.audioMixMode$) = "right" then player.SetAudioMode(4) else player.SetAudioMode(1) endif else player.SetAudioOutput(m.audioOutput%) player.SetAudioMode(m.audioMode%) endif End Sub Sub InitializeVideoZoneObjects() m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m ' create players ' reclaim memory (destroy any leaked video players) RunGarbageCollector() videoPlayer = CreateObject("roVideoPlayer") if type(videoPlayer) <> "roVideoPlayer" then print "videoPlayer creation failed" : stop videoPlayer.SetRectangle(zoneHSM.rectangle) videoInput = CreateObject("roVideoInput") zoneHSM.videoPlayer = videoPlayer zoneHSM.videoInput = videoInput zoneHSM.isVideoZone = true zoneHSM.videoVolume% = zoneHSM.initialVideoVolume% zoneHSM.audioVolume% = zoneHSM.initialAudioVolume% zoneHSM.videoChannelVolumes = CreateObject("roArray", 6, true) zoneHSM.audioChannelVolumes = CreateObject("roArray", 6, true) for i% = 0 to 5 zoneHSM.videoChannelVolumes[i%] = zoneHSM.videoVolume% zoneHSM.audioChannelVolumes[i%] = zoneHSM.audioVolume% next ' initialize video player parameters videoPlayer.SetPort(zoneHSM.msgPort) videoPlayer.SetViewMode(zoneHSM.viewMode%) videoPlayer.SetLoopMode(false) m.SetAudioOutputAndMode(videoPlayer) ' videoPlayer.SetAudioOutput(zoneHSM.audioOutput%) ' videoPlayer.SetAudioMode(zoneHSM.audioMode%) videoPlayer.MapStereoOutput(zoneHSM.audioMapping%) videoPlayer.SetStereoMappingSpan(zoneHSM.audioMappingSpan%) videoPlayer.SetVolume(zoneHSM.videoVolume%) ' Panther only zoneHSM.ConfigureAudioResources() ' Cheetah only if m.bsp.sysInfo.deviceFamily$ = "cheetah" then if zoneHSM.zOrderFront then videoPlayer.ToFront() else videoPlayer.ToBack() endif endif zoneHSM.videoPlayerAudioSettings = CreateObject("roAssociativeArray") zoneHSM.videoPlayerAudioSettings.audioOutput% = zoneHSM.audioOutput% zoneHSM.videoPlayerAudioSettings.stereoMapping% = zoneHSM.audioMapping% zoneHSM.videoPlayerAudioSettings.audioMappingSpan% = zoneHSM.audioMappingSpan% m.bsp.SetAudioVolumeLimits(zoneHSM, zoneHSM.videoPlayerAudioSettings) ' initialize live video parameters videoInput.SetInput(zoneHSM.videoInput$) videoInput.SetStandard(zoneHSM.videoStandard$) videoInput.SetControlValue("brightness", zoneHSM.brightness%) videoInput.SetControlValue("contrast", zoneHSM.contrast%) videoInput.SetControlValue("saturation", zoneHSM.saturation%) videoInput.SetControlValue("hue", zoneHSM.hue%) ' initialize tuner parameter m.currentChannelIndex% = 0 m.firstTuneToChannel = true m.activeState = m.playlist.firstState if type(m.playlist.firstState) = "roAssociativeArray" then m.previousStateName$ = m.playlist.firstState.id$ else m.previousStateName$ = "" endif End Sub Sub VideoZoneConstructor() activeState = m.InitializeVideoZoneObjects() m.CreateObjects() End Sub Function VideoZoneGetInitialState() As Object return m.activeState End Function Function STStreamPlayingEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(m.stateMachine.audioPlayer) = "roAudioPlayer" or type(m.stateMachine.audioPlayer) = "roAudioPlayerMx" then audioPlayer = m.stateMachine.audioPlayer else audioPlayer = m.stateMachine.videoPlayer endif if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) url$ = m.url.GetCurrentParameterValue() m.rtspStream = CreateObject("roRtspStream", url$) if m.mediaType$ = "video" then aa = {} aa["Rtsp"] = m.rtspStream if m.bsp.sign.isVideoWall and m.bsp.sign.videoWallType$ = "stretched" then aa["MultiscreenWidth"] = m.bsp.sign.videoWallNumColumns% aa["MultiscreenHeight"] = m.bsp.sign.videoWallNumRows% aa["MultiscreenX"] = m.bsp.sign.videoWallColumnPosition% aa["MultiscreenY"] = m.bsp.sign.videoWallRowPosition% aa["Mode"] = m.stateMachine.viewMode% endif loopMode% = 1 if type(m.videoEndEvent) = "roAssociativeArray" then loopMode% = 0 m.stateMachine.videoPlayer.SetLoopMode(loopMode%) ok = m.stateMachine.videoPlayer.PlayFile(aa) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error playing rtsp file in STStreamPlayingEventHandler: url = " + url$) videoStreamPlaybackFailure = CreateObject("roAssociativeArray") videoStreamPlaybackFailure["EventType"] = "VideoStreamPlaybackFailure" m.stateMachine.msgPort.PostMessage(videoStreamPlaybackFailure) endif else ok = audioPlayer.PlayFile({Rtsp: m.rtspStream}) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error playing rtsp file in STStreamPlayingEventHandler: url = " + url$) audioStreamPlaybackFailure = CreateObject("roAssociativeArray") audioStreamPlaybackFailure["EventType"] = "AudioStreamPlaybackFailure" m.stateMachine.msgPort.PostMessage(audioStreamPlaybackFailure) endif endif m.bsp.SetTouchRegions(m) m.stateMachine.ClearImagePlane() m.LaunchTimer() ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "stream") ' playback logging m.stateMachine.LogPlayStart("stream", url$) return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else if event["EventType"] = "VideoStreamPlaybackFailure" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else if event["EventType"] = "AudioStreamPlaybackFailure" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roVideoEvent" and type(m.stateMachine.videoPlayer) = "roVideoPlayer" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif endif else if type(event) = "roAudioEvent" and type(m.stateMachine.audioPlayer) = "roAudioPlayer" and event.GetSourceIdentity() = audioPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function STMjpegPlayingEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) if type(m.stateMachine.mjpegUrl) <> "roUrlTransfer" then m.stateMachine.mjpegUrl = CreateObject("roUrlTransfer") endif url$ = m.url.GetCurrentParameterValue() m.stateMachine.mjpegUrl.SetURL(url$) m.stateMachine.mjpegUrl.SetProxy("") if type(m.stateMachine.mjpegMimeStream) <> "roMimeStream" then binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for mjpegMimeStream is " + stri(binding%)) ok = m.stateMachine.mjpegUrl.BindToInterface(binding%) if not ok then stop m.stateMachine.mjpegMimeStream = CreateObject("roMimeStream", m.stateMachine.mjpegUrl) endif if type(m.stateMachine.mjpegVideoPlayer) <> "roVideoPlayer" then m.stateMachine.mjpegVideoPlayer = CreateObject("roVideoPlayer") m.stateMachine.mjpegVideoPlayer.SetRectangle(m.stateMachine.rectangle) m.stateMachine.mjpegVideoPlayer.SetPort(m.bsp.msgPort) endif aa = {} aa["PictureStream"] = m.stateMachine.mjpegMimeStream aa["Rotate"] = m.rotation% if m.bsp.sign.isVideoWall and m.bsp.sign.videoWallType$ = "stretched" then aa["MultiscreenWidth"] = m.bsp.sign.videoWallNumColumns% aa["MultiscreenHeight"] = m.bsp.sign.videoWallNumRows% aa["MultiscreenX"] = m.bsp.sign.videoWallColumnPosition% aa["MultiscreenY"] = m.bsp.sign.videoWallRowPosition% aa["Mode"] = m.stateMachine.viewMode% endif ok = m.stateMachine.mjpegVideoPlayer.PlayFile(aa) m.bsp.SetTouchRegions(m) m.stateMachine.ClearImagePlane() m.LaunchTimer() ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "mjpeg") ' playback logging m.stateMachine.LogPlayStart("mjpeg", url$) return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.stateMachine.mjpegUrl = invalid m.stateMachine.mjpegMimeStream = invalid m.stateMachine.mjpegVideoPlayer = invalid m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) ' else if event["EventType"] = "VideoPlaybackFailureEvent" then ' if type(m.videoEndEvent) = "roAssociativeArray" then ' return m.ExecuteTransition(m.videoEndEvent, stateData, "") ' endif else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.mjpegVideoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function STVideoPlayingEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 VIDEO_TIME_CODE = 12 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.LaunchVideo("video") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else if event["EventType"] = "VideoPlaybackFailureEvent" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") else if not(type(m.synchronizeEvents) = "roAssociativeArray" or type(m.internalSynchronizeEvents) = "roAssociativeArray") then ' looping video - since LaunchVideo is not called, perform logging here. file$ = m.videoItem.fileName$ m.stateMachine.LogPlayStart("video", file$) endif PostMediaEndEvent(m.bsp.msgPort) else if event.GetInt() = VIDEO_TIME_CODE then videoTimeCodeIndex$ = str(event.GetData()) m.bsp.diagnostics.PrintDebug("Video TimeCode Event " + videoTimeCodeIndex$) if type(m.videoTimeCodeEvents) = "roAssociativeArray" then videoTimeCodeEvent = m.videoTimeCodeEvents[videoTimeCodeIndex$] if type(videoTimeCodeEvent) = "roAssociativeArray" then m.bsp.ExecuteTransitionCommands(m.stateMachine, videoTimeCodeEvent) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "videoTimeCode", "", "1") return "HANDLED" endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "videoTimeCode", "", "0") endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub PostMediaEndEvent(msgPort As Object) mediaEndEvent = { } mediaEndEvent["EventType"] = "MEDIA_END" msgPort.PostMessage(mediaEndEvent) End Sub Sub SetVideoTimeCodeEvents() m.stateMachine.videoPlayer.ClearEvents() if type(m.videoTimeCodeEvents) = "roAssociativeArray" then for each eventNum in m.videoTimeCodeEvents m.AddVideoTimeCodeEvent(m.videoTimeCodeEvents[eventNum].timeInMS%, int(val(eventNum))) next endif End Sub Sub AddVideoTimeCodeEvent(timeInMS% As Integer, eventNum% As Integer) if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.AddEvent(eventNum%, timeInMS%) endif End Sub 'endregion 'region Video or Images State Machine ' ************************************************* ' ' VideoOrImages State Machine ' ' ************************************************* Function newVideoOrImagesZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newVideoZoneHSM(bsp, zoneXML) zoneHSM.ConstructorHandler = VideoOrImagesZoneConstructor zoneHSM.InitialPseudostateHandler = VideoOrImagesZoneGetInitialState zoneHSM.imageMode% = GetImageModeValue(zoneXML.zoneSpecificParameters.imageMode.GetText()) zoneHSM.numImageItems% = 0 return zoneHSM End Function Sub LoadImageBuffers() stateTable = m.stateTable for each stateName in stateTable state = stateTable[stateName] if type(state.imageItem) = "roAssociativeArray" then imageItem = state.imageItem if imageItem.useImageBuffer then m.AddImageBufferItem(imageItem.fileName$) endif else if state.type$ = "audioIn" then if state.useImageBuffer then m.AddImageBufferItem(state.imageFileName$) endif else if state.type$ = "interactiveMenuItem" then if state.backgroundImage$ <> "" and state.backgroundImageUseImageBuffer then m.AddImageBufferItem(state.backgroundImage$) endif if type(state.interactiveMenuItems) = "roArray" then for each interactiveMenuItem in state.interactiveMenuItems if interactiveMenuItem.selectedImageUseImageBuffer then m.AddImageBufferItem(interactiveMenuItem.selectedImage$) endif if interactiveMenuItem.selectedImageUseImageBuffer then m.AddImageBufferItem(interactiveMenuItem.unselectedImage$) endif if interactiveMenuItem.targetType$ = "mediaFile" and IsString(interactiveMenuItem.targetImageFile$) and interactiveMenuItem.targetImageFile$ <> "" and interactiveMenuItem.targetImageFileUseImageBuffer then m.AddImageBufferItem(interactiveMenuItem.targetImageFile$) endif next endif endif next End Sub Sub AddImageBufferItem(fileName$ As String) filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, fileName$) if type(m.bsp.imageBuffers) <> "roAssociativeArray" then m.bsp.imageBuffers = CreateObject("roAssociativeArray") endif if not m.bsp.imageBuffers.DoesExist(filePath$) then imageBuffer = CreateObject("roImageBuffer", 0, filePath$) m.bsp.imageBuffers.AddReplace(filePath$, imageBuffer) endif End Sub Sub ClearImageBuffers() if type(m.imageBuffers) = "roAssociativeArray" then for each imageBuffer in m.imageBuffers imageBuffer = invalid next m.imageBuffers = invalid endif End Sub Sub VideoOrImagesZoneConstructor() m.InitializeVideoZoneObjects() zoneHSM = m ' create players if zoneHSM.numImageItems% > 0 then imagePlayer = CreateObject("roImageWidget", zoneHSM.rectangle) zoneHSM.imagePlayer = imagePlayer ' initialize image player parameters imagePlayer.SetDefaultMode(zoneHSM.imageMode%) m.LoadImageBuffers() else zoneHSM.imagePlayer = invalid endif zoneHSM.audioVolume% = zoneHSM.initialAudioVolume% audioInput = CreateObject("roAudioInput", m.bsp.sign.audioInSampleRate%) if type(audioInput) = "roAudioInput" then zoneHSM.audioInput = audioInput endif zoneHSM.audioPlayerAudioSettings = CreateObject("roAssociativeArray") zoneHSM.audioPlayerAudioSettings.audioOutput% = zoneHSM.audioOutput% zoneHSM.audioPlayerAudioSettings.stereoMapping% = zoneHSM.audioMapping% m.bsp.SetAudioVolumeLimits(zoneHSM, zoneHSM.audioPlayerAudioSettings) m.CreateObjects() End Sub Function VideoOrImagesZoneGetInitialState() As Object return m.activeState End Function Sub PopulatePlayFileFromLiveDataFeed() if type(m.liveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then numFiles% = m.liveDataFeed.fileUrls.Count() for i% = 0 to numFiles% - 1 key$ = m.liveDataFeed.fileKeys[i%] url$ = m.liveDataFeed.fileUrls[i%] filePath$ = m.liveDataFeed.assetPoolFiles.GetPoolFilePath(url$) fileTableEntry = CreateObject("roAssociativeArray") fileTableEntry.fileName$ = url$ fileTableEntry.filePath$ = filePath$ fileTableEntry.fileType$ = m.mediaType$ fileTableEntry.videoDisplayMode% = 0 m.filesTable.AddReplace(key$, fileTableEntry) next endif End Sub Function STPlayFileEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.bsp.diagnostics.PrintDebug(m.id$ + ": payload is " + m.payload$) if m.filesTable.IsEmpty() or not m.filesTable.DoesExist(m.payload$) then if m.filesTable.IsEmpty() then m.bsp.diagnostics.PrintDebug(m.id$ + ": files not loaded yet") else m.bsp.diagnostics.PrintDebug(m.id$ + ": no file associated with payload") endif m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.LaunchTimer() m.bsp.SetTouchRegions(m) else fileTableEntry = m.filesTable.Lookup(m.payload$) fileName$ = fileTableEntry.fileName$ fileType$ = fileTableEntry.fileType$ m.imageItem = invalid m.videoItem = invalid m.audioItem = invalid if fileType$ = "image" then m.imageItem = CreateObject("roAssociativeArray") m.imageItem.fileName$ = fileName$ if type(fileTableEntry.filePath$) = "roString" then m.imageItem.filePath$ = fileTableEntry.filePath$ endif m.imageItem.slideTransition% = m.slideTransition% m.imageItem.useImageBuffer = false m.imageItem.userVariable = fileTableEntry.userVariable m.DisplayImage("playFile") else if fileType$ = "video" then m.videoItem = CreateObject("roAssociativeArray") m.videoItem.fileName$ = fileName$ if type(fileTableEntry.filePath$) = "roString" then m.videoItem.filePath$ = fileTableEntry.filePath$ endif m.videoItem.probeData = fileTableEntry.probeData m.videoItem.videoDisplayMode% = fileTableEntry.videoDisplayMode% m.videoItem.userVariable = fileTableEntry.userVariable m.LaunchVideo("playFile") else if fileType$ = "audio" then m.audioItem = CreateObject("roAssociativeArray") m.audioItem.fileName$ = fileName$ if type(fileTableEntry.filePath$) = "roString" then m.audioItem.filePath$ = fileTableEntry.filePath$ endif m.audioItem.probeData = fileTableEntry.probeData m.audioItem.userVariable = fileTableEntry.userVariable m.LaunchAudio("playFile") endif endif return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) return "HANDLED" endif endif else if type(m.videoItem) = "roAssociativeArray" and type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else if type(m.audioItem)="roAssociativeArray" and IsAudioEvent(m.stateMachine, event) then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif endif return m.MediaItemEventHandler(event, stateData) End Function ' this code explicitly does not catch roAudioEventMx events - those are handled elsewhere. Function IsAudioEvent(stateMachine As Object, event As Object) As Boolean return (type(stateMachine.audioPlayer)="roAudioPlayer" and type(event) = "roAudioEvent" and event.GetSourceIdentity() = stateMachine.audioPlayer.GetIdentity()) or (type(stateMachine.videoPlayer)="roVideoPlayer" and type(event) = "roVideoEvent" and event.GetSourceIdentity() = stateMachine.videoPlayer.GetIdentity()) End Function Function IsAudioPlayer(audioPlayer As Object) As Boolean return type(audioPlayer) = "roAudioPlayer" or type(audioPlayer) = "roAudioPlayerMx" End Function Sub ConfigureIntraStateEventHandlerButton(navigation As Object) if type(navigation) = "roAssociativeArray" then if type(navigation.bpUserEventButtonPanelIndex$) = "roString" and type(navigation.bpUserEventButtonNumber$) = "roString" then bpEvent = { } bpEvent.buttonPanelIndex% = int(val(navigation.bpUserEventButtonPanelIndex$)) bpEvent.buttonNumber$ = navigation.bpUserEventButtonNumber$ bpEvent.configuration$ = "press" m.bsp.ConfigureBPButton(bpEvent.buttonPanelIndex%, bpEvent.buttonNumber$, bpEvent) endif if type(navigation.gpioUserEventButtonNumber$) = "roString" then gpioEvent = { } gpioEvent.buttonNumber$ = navigation.gpioUserEventButtonNumber$ gpioEvent.configuration$ = "press" m.bsp.ConfigureGPIOButton(gpioEvent.buttonNumber$, gpioEvent) endif endif End Sub Sub PopulateMediaListFromLiveDataFeed() if type(m.liveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then m.numItems% = m.liveDataFeed.fileUrls.Count() m.items = CreateObject("roArray", m.numItems%, true) for each url in m.liveDataFeed.fileUrls filePath$ = m.liveDataFeed.assetPoolFiles.GetPoolFilePath(url) item = {} item.filePath$ = filePath$ if m.mediaType$ = "image" then item.slideTransition% = 0 item.useImageBuffer = false else if m.mediaType$ = "video" then item.videoDisplayMode% = 0 endif m.items.push(item) next m.playbackIndices = CreateObject("roArray", m.numItems%, true) for i% = 0 to m.numItems%-1 m.playbackIndices[i%] = i% next endif End Sub Sub ShuffleMediaListContent() randomNumbers = CreateObject("roArray", m.numItems%, true) for each item in m.items randomNumbers.push(rnd(10000)) next numItemsToSort% = m.numItems% for i% = numItemsToSort% - 1 to 1 step -1 for j% = 0 to i%-1 index0 = m.playbackIndices[j%] value0 = randomNumbers[index0] index1 = m.playbackIndices[j%+1] value1 = randomNumbers[index1] if value0 > value1 then k% = m.playbackIndices[j%] m.playbackIndices[j%] = m.playbackIndices[j%+1] m.playbackIndices[j%+1] = k% endif next next End Sub Function STDisplayingMediaListItemEventHandler(event As Object, stateData As Object) As Object MEDIA_START = 3 MEDIA_END = 8 MEDIA_ERROR = 16 VIDEO_TIME_CODE = 12 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' if using a live data feed, populate items here if type(m.liveDataFeed) = "roAssociativeArray" then ' ensure that live data feed content has been loaded if type(m.liveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then m.PopulateMediaListFromLiveDataFeed() endif endif if type(m.bsp.mediaListInactivity) = "roAssociativeArray" then if type(m.bsp.mediaListInactivity.timer) = "roTimer" then m.bsp.mediaListInactivity.timer.Stop() else m.bsp.mediaListInactivity.timer = CreateObject("roTimer") m.bsp.mediaListInactivity.timer.SetPort(m.bsp.msgPort) endif endif m.ConfigureIntraStateEventHandlerButton(m.nextNavigation) m.ConfigureIntraStateEventHandlerButton(m.previousNavigation) m.firstItemDisplayed = false ' reset playback index if appropriate if m.playFromBeginning then m.playbackIndex% = 0 endif if m.numItems% > 0 then m.playbackActive = true ' reshuffle media list if appropriate if m.playbackIndex% = 0 and m.shuffle then m.ShuffleMediaListContent() endif m.AdvanceMediaListPlayback(true) else m.playbackActive = false endif return "HANDLED" else if event["EventType"] = "VideoPlaybackFailureEvent" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else if event["EventType"] = "AudioPlaybackFailureEvent" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif else if event["EventType"] = "CONTENT_DATA_FEED_LOADED" then if type(m.liveDataFeed) = "roAssociativeArray" and event["Name"] = m.liveDataFeed.name$ then m.playbackIndex% = 0 m.PopulateMediaListFromLiveDataFeed() if m.shuffle then m.ShuffleMediaListContent() endif if not m.playbackActive then m.playbackActive = true m.AdvanceMediaListPlayback(true) endif return "HANDLED" endif else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.StartInactivityTimer() m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) return "HANDLED" endif endif else if m.mediaType$ = "video" and type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if m.advanceOnMediaEnd then if not(m.playbackIndex% = 0 and type(m.videoEndEvent) = "roAssociativeArray") then m.AdvanceMediaListPlayback(true) return "HANDLED" endif endif if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else if event.GetInt() = VIDEO_TIME_CODE then videoTimeCodeIndex$ = str(event.GetData()) m.bsp.diagnostics.PrintDebug("Video TimeCode Event " + videoTimeCodeIndex$) if type(m.videoTimeCodeEvents) = "roAssociativeArray" then videoTimeCodeEvent = m.videoTimeCodeEvents[videoTimeCodeIndex$] if type(videoTimeCodeEvent) = "roAssociativeArray" then m.bsp.ExecuteTransitionCommands(m.stateMachine, videoTimeCodeEvent) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "videoTimeCode", "", "1") return "HANDLED" endif endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "videoTimeCode", "", "0") endif else if m.mediaType$ = "audio" and m.stateMachine.type$ = "EnhancedAudio" and type(event) = "roAudioEventMx" then m.bsp.diagnostics.PrintDebug("AudioMx Event" + stri(event.GetInt())) if event.GetInt() = MEDIA_START then if event.GetSourceIdentity() = m.stateMachine.audioPlayer.GetIdentity() then ' index of track that just started playing currentTrackIndex% = int(val(event.GetUserData())) ' get index of track to queue m.playbackIndex% = currentTrackIndex% + 1 if m.playbackIndex% >= m.numItems% then m.playbackIndex% = 0 endif m.audioItem = m.items[m.playbackIndices[m.playbackIndex%]] if not(m.playbackIndex% = 0 and type(m.audioEndEvent) = "roAssociativeArray") then m.PlayMixerAudio(false, m.playbackIndex%, false) endif ' at this point, m.playbackIndex% points to both the item that is queued as well as the next item to play - the concept of ' "next item to play" is needed for NextNavigation, BackNavigation, and re-entering the state ' m.AdvanceMediaListPlayback(false) return "HANDLED" endif else if event.GetInt() = MEDIA_END then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else if m.mediaType$ = "audio" and m.stateMachine.type$ <> "EnhancedAudio" and IsAudioEvent(m.stateMachine, event) then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if m.advanceOnMediaEnd then if not(m.playbackIndex% = 0 and type(m.audioEndEvent) = "roAssociativeArray") then m.AdvanceMediaListPlayback(true) return "HANDLED" endif endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else if type(event) = "roTimerEvent" then if m.advanceOnImageTimeout then if type(m.advanceOnImageTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.advanceOnImageTimeoutTimer.GetIdentity() then if m.playbackIndex% <> 0 or type(m.mstimeoutEvent) <> "roAssociativeArray" then m.AdvanceMediaListPlayback(true) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeout", "", "1") return "HANDLED" endif endif if m.playbackIndex% = 0 and type(m.mstimeoutEvent) = "roAssociativeArray" and type(m.advanceOnImageTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.advanceOnImageTimeoutTimer.GetIdentity() then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeout", "", "1") return m.ExecuteTransition(m.mstimeoutEvent, stateData, "") endif return m.MediaItemEventHandler(event, stateData) endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeout", "", "0") endif if type(m.nextNavigation) = "roAssociativeArray" and m.playbackActive then advance = m.HandleIntraStateEvent(event, m.nextNavigation) if advance then m.AdvanceMediaListPlayback(true) return "HANDLED" endif endif if type(m.previousNavigation) = "roAssociativeArray" and m.playbackActive then retreat = m.HandleIntraStateEvent(event, m.previousNavigation) if retreat then m.RetreatMediaListPlayback(true) return "HANDLED" endif endif return m.MediaItemEventHandler(event, stateData) End Function Function HandleIntraStateEvent(event As Object, navigation As Object) As Boolean if type(event) = "roAssociativeArray" and IsString(event["EventType"]) then if event["EventType"] = "BPControlDown" and IsString(navigation.bpUserEventButtonNumber$) then bpIndex$ = event["ButtonPanelIndex"] bpNum$ = event["ButtonNumber"] m.bsp.diagnostics.PrintDebug("BP Press, button number " + bpNum$ + ", button index " + bpIndex$) if navigation.bpUserEventButtonNumber$ = bpNum$ and navigation.bpUserEventButtonPanelIndex$ = bpIndex$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return true endif else if event["EventType"] = "GPIOControlDown" and IsString(navigation.gpioUserEventButtonNumber$) then gpioNum$ = event["ButtonNumber"] m.bsp.diagnostics.PrintDebug("GPIO Press, button number " + gpioNum$) if navigation.gpioUserEventButtonNumber$ = gpioNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioDown", gpioNum$, "1") return true endif else if event["EventType"] = "SEND_ZONE_MESSAGE" and IsString(navigation.zoneMessageUserEvent$) then zoneMessage$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("ZoneMessageEvent " + zoneMessage$) if navigation.zoneMessageUserEvent$ = zoneMessage$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "zoneMessage", zoneMessage$, "1") return true endif endif endif if type(event) = "roControlDown" and stri(event.GetSourceIdentity()) = stri(m.bsp.controlPort.GetIdentity()) and IsString(navigation.gpioUserEvent$) then gpioNum$ = StripLeadingSpaces(str(event.GetInt())) m.bsp.diagnostics.PrintDebug("Button Press" + gpioNum$) if navigation.gpioUserEvent$ = gpioNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return true endif endif if type(event) = "roDatagramEvent" and IsString(navigation.udpUserEvent$) then udpEvent$ = event.GetString() m.bsp.diagnostics.PrintDebug("UDP Event" + udpEvent$) if navigation.udpUserEvent$ = udpEvent$ or navigation.udpEvent$ = "" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", udpEvent$, "1") return true else if instr(1, navigation.udpUserEvent$, "(.*)") > 0 then r = CreateObject("roRegEx", navigation.udpUserEvent$, "i") if type(r) = "roRegex" then matches = r.match(udpEvent$) if matches.Count() > 0 then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "udp", udpEvent$, "1") return true endif endif endif endif endif if type(event) = "roKeyboardPress" and IsString(navigation.keyboardUserEvent$) then keyboardChar$ = chr(event.GetInt()) m.bsp.diagnostics.PrintDebug("Keyboard Press" + keyboardChar$) ' if keyboard input is non printable character, convert it to the special code keyboardCode$ = m.bsp.GetNonPrintableKeyboardCode(event.GetInt()) if keyboardCode$ <> "" then keyboardChar$ = keyboardCode$ endif if navigation.keyboardUserEvent$ = keyboardChar$ or navigation.keyboardUserEvent$ = "" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "keyboard", keyboardChar$, "1") return true endif endif if type(event) = "roIRRemotePress" and IsString(navigation.remoteUserEvent$) then m.bsp.diagnostics.PrintDebug("Remote Event" + stri(event.GetInt())) remoteEvent% = event.GetInt() remoteEvent$ = ConvertToRemoteCommand(remoteEvent%) if navigation.remoteUserEvent$ = remoteEvent$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "remote", remoteEvent$, "1") return true endif endif if type(event) = "roStreamLineEvent" and IsString(navigation.serialUserEventPort$) and IsString(navigation.serialUserEventSerialEvent$) then port$ = event.GetUserData() serialEvent$ = event.GetString() m.bsp.diagnostics.PrintDebug("Serial Line Event " + event.GetString()) if port$ = navigation.serialUserEventPort$ then if (serialEvent$ = navigation.serialUserEventSerialEvent$ or navigation.serialUserEventSerialEvent$ = "<*>") then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", serialEvent$, "1") return true else if instr(1, navigation.serialUserEventSerialEvent$, "(.*)") > 0 then r = CreateObject("roRegEx", navigation.serialUserEventSerialEvent$, "i") if type(r) = "roRegex" then matches = r.match(serialEvent$) if matches.Count() > 0 then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", serialEvent$, "1") return true endif endif endif endif endif endif return false End Function Sub LaunchMediaListPlaybackItem(playImmediate As Boolean) ' get current media item and launch playback item = m.items[m.playbackIndices[m.playbackIndex%]] if m.mediaType$ = "image" then m.imageItem = item if not m.firstItemDisplayed then m.PreDrawImage() endif m.DrawImage(true) if not m.firstItemDisplayed then m.PostDrawImage("imageList") endif ' if advancing on image timeout, set the timer if type(m.advanceOnImageTimeoutTimer) = "roTimer" then m.advanceOnImageTimeoutTimer.Stop() endif if m.advanceOnImageTimeout then if type(m.advanceOnImageTimeoutTimer) <> "roTimer" then m.advanceOnImageTimeoutTimer = CreateObject("roTimer") m.advanceOnImageTimeoutTimer.SetPort(m.stateMachine.msgPort) endif m.advanceOnImageTimeoutTimer.SetElapsed(0, m.imageTimeout) m.advanceOnImageTimeoutTimer.Start() endif else if m.mediaType$ = "video" then m.videoItem = item if not m.firstItemDisplayed then m.PrePlayVideo() endif m.PlayVideo(not m.firstItemDisplayed, true) if not m.firstItemDisplayed then m.PostPlayVideo("videoList") endif else if m.mediaType$ = "audio" then m.audioItem = item if not m.firstItemDisplayed then m.PrePlayAudio() endif if m.stateMachine.type$ = "EnhancedAudio" then m.PlayMixerAudio(not m.firstItemDisplayed, m.playbackIndex%, playImmediate) else m.PlayAudio(not m.firstItemDisplayed) endif if not m.firstItemDisplayed then m.PostPlayAudio("audioList") endif endif m.firstItemDisplayed = true End Sub Sub AdvanceMediaListPlayback(playImmediate As Boolean) m.LaunchMediaListPlaybackItem(playImmediate) m.playbackIndex% = m.playbackIndex% + 1 if m.playbackIndex% >= m.numItems% then m.playbackIndex% = 0 endif End Sub Sub RetreatMediaListPlayback(playImmediate As Boolean) ' index currently points to 'next' track - need to retreat by 2 to get to previous track for i% = 0 to 1 m.playbackIndex% = m.playbackIndex% - 1 if m.playbackIndex% < 0 then m.playbackIndex% = m.numItems% - 1 endif next m.LaunchMediaListPlaybackItem(playImmediate) m.playbackIndex% = m.playbackIndex% + 1 if m.playbackIndex% >= m.numItems% then m.playbackIndex% = 0 endif End Sub Sub StartInactivityTimer() if m.bsp.inactivityTimeout then if type(m.bsp.mediaListInactivity) = "roAssociativeArray" then if type(m.bsp.mediaListInactivity.timer) = "roTimer" then m.bsp.mediaListInactivity.timer.SetElapsed(m.bsp.inactivityTime%, 0) m.bsp.mediaListInactivity.timer.SetPort(m.bsp.msgPort) m.bsp.mediaListInactivity.timer.Start() endif endif endif End Sub Sub ConfigureBPButtons() for buttonPanelIndex% = 0 to 2 bpEvents = m.bpEvents[buttonPanelIndex%] for each buttonNumber in bpEvents bpEvent = bpEvents[buttonNumber] m.bsp.ConfigureBPButton(buttonPanelIndex%, buttonNumber, bpEvent) next next End Sub Sub ConfigureGPIOButtons() gpioEvents = m.gpioEvents for each buttonNumber in gpioEvents gpioEvent = gpioEvents[buttonNumber] m.bsp.ConfigureGPIOButton(buttonNumber, gpioEvent) next End Sub Sub PrePlayVideo() m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" End Sub Sub PlayVideo(executeEntryCmds As Boolean, disableLoopMode As Boolean) ' set video mode before executing commands - required order for working around LG (maybe others) bugs getting back to 2-D mode videoMode = CreateObject("roVideoMode") videoMode.Set3dMode(m.videoItem.videoDisplayMode%) videoMode = invalid loopMode% = 1 if disableLoopMode or type(m.videoEndEvent) = "roAssociativeArray" or type(m.synchronizeEvents) = "roAssociativeArray" or type(m.internalSynchronizeEvents) = "roAssociativeArray" then loopMode% = 0 m.stateMachine.videoPlayer.SetLoopMode(loopMode%) if type(m.videoItem.filePath$) = "roString" then file$ = m.videoItem.filePath$ filePath$ = m.videoItem.filePath$ else file$ = m.videoItem.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) endif ' determine whether or not a preload has been performed preloaded = false if type(m.stateMachine.preloadState) = "roAssociativeArray" then if m.stateMachine.preloadedStateName$ = m.name$ then preloaded = true endif endif syncInProgress = false if type(m.stateMachine.syncInfo) = "roAssociativeArray" then syncInProgress = true endif m.stateMachine.videoPlayer.EnableSafeRegionTrimming(false) if not preloaded and not syncInProgress then m.stateMachine.videoPlayer.Stop() endif m.SetVideoTimeCodeEvents() if executeEntryCmds then m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) endif if preloaded then ok = m.stateMachine.videoPlayer.Play() if ok=0 print "Error Playing (supposedly) Preloaded File: " ; file$ : stop m.stateMachine.preloadState = invalid m.stateMachine.preloadedStateName$ = "" m.bsp.diagnostics.PrintDebug("LaunchVideo: play preloaded file " + file$ + ", loopMode = " + str(loopMode%)) else aa = { } aa.AddReplace("Filename", filePath$) if type(m.videoItem.probeData) = "roString" then if m.bsp.sysInfo.deviceModel$ <> "4K1440" then m.bsp.diagnostics.PrintDebug("LaunchVideo: probeData = " + m.videoItem.probeData) aa.AddReplace("ProbeString", m.videoItem.probeData) endif endif if syncInProgress then aa.AddReplace("SyncDomain", m.stateMachine.syncInfo.SyncDomain) aa.AddReplace("SyncId", m.stateMachine.syncInfo.SyncId) aa.AddReplace("SyncIsoTimestamp", m.stateMachine.syncInfo.SyncIsoTimestamp) if m.bsp.sign.isVideoWall and m.bsp.sign.videoWallType$ = "stretched" then aa["MultiscreenWidth"] = m.bsp.sign.videoWallNumColumns% aa["MultiscreenHeight"] = m.bsp.sign.videoWallNumRows% aa["MultiscreenX"] = m.bsp.sign.videoWallColumnPosition% aa["MultiscreenY"] = m.bsp.sign.videoWallRowPosition% aa["Mode"] = m.stateMachine.viewMode% endif endif ok = m.stateMachine.videoPlayer.PlayFile(aa) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error playing file in LaunchVideo: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) videoPlaybackFailure = CreateObject("roAssociativeArray") videoPlaybackFailure["EventType"] = "VideoPlaybackFailureEvent" m.stateMachine.msgPort.PostMessage(videoPlaybackFailure) endif if syncInProgress then m.bsp.diagnostics.PrintDebug("LaunchVideo: play synchronized file " + file$) m.stateMachine.syncInfo = invalid endif endif if type(m.videoItem.userVariable) = "roAssociativeArray" then m.videoItem.userVariable.Increment() endif ' playback logging m.stateMachine.LogPlayStart("video", file$) End Sub Sub PostPlayVideo(stateType$ As String) m.bsp.SetTouchRegions(m) m.stateMachine.ClearImagePlane() m.LaunchTimer() ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, stateType$) End Sub Sub LaunchVideo(stateType$ As String) m.PrePlayVideo() m.PlayVideo(true, false) m.PostPlayVideo(stateType$) End Sub Sub PreDrawImage() m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) End Sub Sub DrawImage( setTransition As Boolean ) if type(m.imageItem.filePath$) = "roString" then file$ = m.imageItem.filePath$ filePath$ = m.imageItem.filePath$ else file$ = m.imageItem.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) endif if setTransition then m.stateMachine.imagePlayer.SetDefaultTransition(m.imageItem.slideTransition%) endif if m.imageItem.useImageBuffer and type(m.bsp.imageBuffers) = "roAssociativeArray" and m.bsp.imageBuffers.DoesExist(filePath$) then m.bsp.diagnostics.PrintDebug("Use imageBuffer for " + file$ + " in DisplayImage: ") imageBuffer = m.bsp.imageBuffers.Lookup(filePath$) m.stateMachine.ClearImagePlane() m.stateMachine.imagePlayer.DisplayBuffer(imageBuffer, 0, 0) else ' m.stateMachine.canvasWidget = invalid if type(m.stateMachine.syncInfo) = "roAssociativeArray" then aa = { } aa.AddReplace("Filename", filePath$) aa.AddReplace("SyncDomain", m.stateMachine.syncInfo.SyncDomain) aa.AddReplace("SyncId", m.stateMachine.syncInfo.SyncId) aa.AddReplace("SyncIsoTimestamp", m.stateMachine.syncInfo.SyncIsoTimestamp) aa.AddReplace("Transition", m.imageItem.slideTransition%) ok = m.stateMachine.imagePlayer.DisplayFile(aa) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error displaying synchronized file: " + file$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) else m.bsp.diagnostics.PrintDebug("DisplayImage: display synchronized file " + file$) endif m.stateMachine.syncInfo = invalid else ' determine whether or not a preload has been performed preloaded = false if type(m.stateMachine.preloadState) = "roAssociativeArray" then if m.stateMachine.preloadedStateName$ = m.name$ preloaded = true m.bsp.diagnostics.PrintDebug("Use preloaded file " + file$ + " in DisplayImage: ") endif endif if not preloaded then ok = m.stateMachine.imagePlayer.PreloadFile(filePath$) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error preloading file in DisplayImage: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) else m.bsp.diagnostics.PrintDebug("Preloaded file in DisplayImage: " + file$) endif endif m.bsp.diagnostics.PrintDebug("DisplayPreload in DisplayImage: " + file$) ok = m.stateMachine.imagePlayer.DisplayPreload() if ok = 0 then m.bsp.diagnostics.PrintDebug("Error in DisplayPreload in DisplayImage: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) endif endif endif m.stateMachine.ShowImageWidget() m.stateMachine.preloadState = 0 m.stateMachine.preloadedStateName$ = "" if type(m.imageItem.userVariable) = "roAssociativeArray" then m.imageItem.userVariable.Increment() endif ' playback logging m.stateMachine.LogPlayStart("image", file$) End Sub Sub PostDrawImage(stateType$ As String) if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, stateType$) End Sub Sub DisplayImage(stateType$ As String) m.PreDrawImage() m.DrawImage(true) m.PostDrawImage(stateType$) End Sub Sub PrePlayAudio() m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif End Sub Sub PlayAudio(executeEntryCmds As Boolean) loopMode% = 1 if type(m.audioEndEvent) = "roAssociativeArray" then loopMode% = 0 if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer else player = m.stateMachine.videoPlayer endif player.SetLoopMode(loopMode%) player.Stop() if executeEntryCmds then m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) endif if type(m.audioItem.filePath$) = "roString" then file$ = m.audioItem.filePath$ filePath$ = m.audioItem.filePath$ else file$ = m.audioItem.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) endif aa = { } aa.AddReplace("Filename", filePath$) if type(m.audioItem.probeData) = "roString" then m.bsp.diagnostics.PrintDebug("LaunchAudio: probeData = " + m.audioItem.probeData) aa.AddReplace("ProbeString", m.audioItem.probeData) endif ok = player.PlayFile(aa) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error playing audio file: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) audioPlaybackFailure = CreateObject("roAssociativeArray") audioPlaybackFailure["EventType"] = "AudioPlaybackFailureEvent" m.stateMachine.msgPort.PostMessage(audioPlaybackFailure) endif m.stateMachine.ClearImagePlane() if type(m.audioItem.userVariable) = "roAssociativeArray" then m.audioItem.userVariable.Increment() endif ' playback logging m.stateMachine.LogPlayStart("audio", file$) End Sub Sub PostPlayAudio(stateType$ As String) m.bsp.SetTouchRegions(m) m.LaunchTimer() ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, stateType$) End Sub Sub LaunchAudio(stateType$ As String) m.PrePlayAudio() m.PlayAudio(true) m.PostPlayAudio(stateType$) End Sub Function STAudioInPlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" videoZone = m.bsp.GetVideoZone(m.stateMachine) if type(videoZone) = "roAssociativeArray" then videoZone.videoPlayer.StopClear() endif m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) file$ = m.imageFileName$ if file$ <> "" then m.stateMachine.imagePlayer.SetDefaultTransition(0) filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) if m.useImageBuffer and type(m.bsp.imageBuffers) = "roAssociativeArray" and m.bsp.imageBuffers.DoesExist(filePath$) then m.bsp.diagnostics.PrintDebug("Use imageBuffer for " + file$ + " in STAudioInPlayingEventHandler: ") imageBuffer = m.bsp.imageBuffers.Lookup(filePath$) m.stateMachine.ClearImagePlane() m.stateMachine.imagePlayer.DisplayBuffer(imageBuffer, 0, 0) else ok = m.stateMachine.imagePlayer.PreloadFile(filePath$) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error preloading file in STAudioInPlayingEventHandler: " + file$ + ", " + filePath$) else m.bsp.diagnostics.PrintDebug("Preloaded file in STAudioInPlayingEventHandler: " + file$) endif m.bsp.diagnostics.PrintDebug("DisplayPreload in STAudioInPlayingEventHandler: " + file$) ok = m.stateMachine.imagePlayer.DisplayPreload() if ok = 0 then m.bsp.diagnostics.PrintDebug("Error in DisplayPreload in STAudioInPlayingEventHandler: " + file$ + ", " + filePath$) endif endif m.stateMachine.ShowImageWidget() endif if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer else player = m.stateMachine.videoPlayer endif player.PlayFile(m.stateMachine.audioInput) m.LaunchTimer() m.bsp.SetTouchRegions(m) if file$ <> "" and type(m.imageUserVariable) = "roAssociativeArray" then m.imageUserVariable.Increment() endif ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "audioIn") ' playback logging m.stateMachine.LogPlayStart("audioIn", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer else player = m.stateMachine.videoPlayer endif player.Stop() m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function STSuperStateEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "superState") ' playback logging m.stateMachine.LogPlayStart("superState", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else if event["EventType"] = "MEDIA_END" then if type(m.mediaEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.mediaEndEvent, stateData, "") endif else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub RestartXModemTimeoutTimer() m.sendTimeoutTimer = CreateObject("roTimer") m.sendTimeoutTimer.SetPort(m.stateMachine.msgPort) m.sendTimeoutTimer.SetElapsed(10, 0) m.sendTimeoutTimer.Start() End Sub Function RetryXModemTransfer() As Boolean if m.attemptedRetries >= m.numberOfRetries% then m.bsp.diagnostics.PrintDebug("retry count exceeded - transfer failure.") if type(m.failEvent) = "roAssociativeArray" then return false endif endif m.attemptedRetries = m.attemptedRetries + 1 return true End Function Function STXModemEventHandler(event As Object, stateData As Object) As Object ' serial constants SOH = 1 EOT = 4 ACK = 6 NAK = 21 CAN = 24 ' states WAITING_FOR_NAK = 0 WAITING_FOR_TRANSFER_ACK = 1 WAITING_FOR_FINAL_ACK = 2 WAITING_FOR_CRC = 3 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "xModem") ' playback logging m.stateMachine.LogPlayStart("xModem", "") ' get relevant file information file$ = m.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) m.xModemFile = CreateObject("roReadFile", filePath$) m.xModemFile.SeekToEnd() fileSize = m.xModemFile.CurrentPosition() m.xModemFile.SeekAbsolute(0) numFullBlocks% = fileSize / 128 m.bytesInFinalBlock% = fileSize - (numFullBlocks% * 128) m.numFullBlocks% = numFullBlocks% m.totalNumBlocks% = m.numFullBlocks% if m.bytesInFinalBlock% > 0 then m.totalNumBlocks% = m.numFullBlocks% + 1 endif m.bsp.diagnostics.PrintDebug("numFullBlocks = " + stri(m.numFullBlocks%)) m.bsp.diagnostics.PrintDebug("bytesInFinalBlock = " + stri(m.bytesInFinalBlock%)) m.serial = m.bsp.serial[m.port$] ' start timeout timer m.RestartXModemTimeoutTimer() m.attemptedRetries = 0 m.serial.SetByteEventPort(m.bsp.msgPort) ' send file name command and wait for NAK m.state = WAITING_FOR_NAK return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.serial.SetByteEventPort(invalid) m.serial.SetLineEventPort(m.bsp.msgPort) m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roTimerEvent" then if type(m.sendTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.sendTimeoutTimer.GetIdentity() then m.bsp.diagnostics.PrintDebug("Xmodem timeout") if type(m.failEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.failEvent, stateData, "") endif endif else if type(event) = "roStreamByteEvent" then serialByte% = event.GetInt() m.bsp.diagnostics.PrintDebug("Serial Byte Event " + str(serialByte%)) if m.state = WAITING_FOR_NAK then if serialByte% = NAK then m.sendCommandTimeoutTimer = invalid m.attemptedRetries = 0 m.bsp.diagnostics.PrintDebug("Waiting for NAK, received NAK") ' start sending data m.currentBlock% = 0 m.blockNumToSend% = 1 m.SendXModemBlock() m.state = WAITING_FOR_TRANSFER_ACK else if serialByte% = CAN then m.bsp.diagnostics.PrintDebug("CAN received while waiting for NAK") if type(m.failEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.failEvent, stateData, "") endif else m.bsp.diagnostics.PrintDebug("Waiting for NAK, received " + stri(serialByte%)) endif ' restart timer m.RestartXModemTimeoutTimer() else if m.state = WAITING_FOR_TRANSFER_ACK then m.bsp.diagnostics.PrintDebug("Waiting for transfer ACK") if serialByte% = ACK then m.bsp.diagnostics.PrintDebug("Received ACK") m.attemptedRetries = 0 ' increment counters m.currentBlock% = m.currentBlock% + 1 m.blockNumToSend% = m.blockNumToSend% + 1 if m.blockNumToSend% > &HFF then m.blockNumToSend% = 0 endif if m.currentBlock% < m.totalNumBlocks% m.SendXModemBlock() else ' close file m.xModemFile = invalid ' send EOT m.serial.SendByte(EOT) m.state = WAITING_FOR_FINAL_ACK endif else if serialByte% = NAK then m.bsp.diagnostics.PrintDebug("Received NAK, resend block") if not m.RetryXModemTransfer() then return m.ExecuteTransition(m.failEvent, stateData, "") endif m.SendXModemBlock() else if serialByte% = CAN then m.bsp.diagnostics.PrintDebug("CAN received while waiting for ACK") if type(m.failEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.failEvent, stateData, "") endif else m.bsp.diagnostics.PrintDebug("Waiting for transfer ACK, received " + stri(serialByte%) + ". Resend block.") if not m.RetryXModemTransfer() then return m.ExecuteTransition(m.failEvent, stateData, "") endif m.SendXModemBlock() endif ' restart timer m.RestartXModemTimeoutTimer() else if m.state = WAITING_FOR_FINAL_ACK then if serialByte% = ACK then m.attemptedRetries = 0 m.state = WAITING_FOR_CRC ' switch to ASCII m.serial.SetLineEventPort(m.bsp.msgPort) else if serialByte% = CAN then m.bsp.diagnostics.PrintDebug("CAN received while waiting for final ACK") if type(m.failEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.failEvent, stateData, "") endif else m.bsp.diagnostics.PrintDebug("Waiting for final ACK, received " + stri(serialByte%) + ". Resend EOT.") if not m.RetryXModemTransfer() then return m.ExecuteTransition(m.failEvent, stateData, "") endif m.serial.SendByte(EOT) endif ' restart timer m.RestartXModemTimeoutTimer() endif else if type(event) = "roStreamLineEvent" then event$ = event.GetString() m.bsp.diagnostics.PrintDebug("Serial line event = " + event$ + ". Looking for crc " + m.crc16$) if m.state = WAITING_FOR_CRC then if instr(1, event$, m.crc16$) > 0 then if type(m.successEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.successEvent, stateData, "") endif endif ' restart timeout timer m.RestartXModemTimeoutTimer() endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub SendXModemBlock() SOH = 1 blockData = CreateObject("roByteArray") m.serial.SendByte(SOH) m.serial.SendByte(m.blockNumToSend%) m.serial.SendByte(255 - m.blockNumToSend%) checkSum% = 0 ' seek to appropriate block (seek is required due to possible retries) m.xModemFile.SeekAbsolute(m.currentBlock% * 128) ' read bytes if m.currentBlock% < m.numFullBlocks% then bytesToRead% = 128 else bytesToRead% = m.bytesInFinalBlock% endif for i% = 0 to 127 if i% < bytesToRead% then value% = m.xModemFile.ReadByte() else value% = &H1A endif blockData[i%] = value% checkSum% = checkSum% + value% end for checkSum% = checkSum% and &HFF m.serial.SendBlock(blockData) m.serial.SendByte(checkSum%) End Sub Function STEventHandlerEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) if m.stopPlayback then if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif m.stateMachine.ClearImagePlane() if IsAudioPlayer(m.stateMachine.audioPlayer) then m.stateMachine.audioPlayer.Stop() endif endif m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "eventHandler") ' playback logging m.stateMachine.LogPlayStart("eventHandler", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function STRFScanHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.stateMachine.videoPlayer.StopClear() m.stateMachine.ClearImagePlane() m.channelManager = CreateObject("roChannelManager") ' m.channelManager.EnableScanDebug("scanDebugOutput.txt") m.channelManager.SetPort(m.bsp.msgPort) eventData$ = "" channelMap = m.scanSpec["ChannelMap"] if type(channelMap) = "roString" and channelMap <> "" then eventData$ = eventData$ + channelMap endif modulationType = m.scanSpec["ModulationType"] if type(modulationType) = "roString" and modulationType <> "" then eventData$ = eventData$ + " " + modulationType endif firstRFChannel = m.scanSpec["FirstRfChannel"] if type(firstRFChannel) = "roString" and firstRFChannel <> "" then eventData$ = eventData$ + " " + firstRFChannel endif lastRFChannel = m.scanSpec["LastRfChannel"] if type(lastRFChannel) = "roString" and lastRFChannel <> "" then eventData$ = eventData$ + " " + lastRFChannel endif m.bsp.diagnostics.PrintDebug("begin scan: " + eventData$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SCAN_START, eventData$) m.UpdateTunerScanPercentageComplete("0") m.channelManager.ClearChannelData() m.channelManager.AsyncScan(m.scanSpec) m.scanInProgress = true m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "tunerScan") ' playback logging m.stateMachine.LogPlayStart("tunerScan", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then if m.scanInProgress then m.bsp.diagnostics.PrintDebug("Cancel tuner scan") m.channelManager.CancelScan() endif m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roChannelManagerEvent" then if event = 0 then m.bsp.diagnostics.PrintDebug("Scan complete") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SCAN_COMPLETE, "") m.scanInProgress = false ' save the current channel savedCurrentChannel = invalid if m.bsp.scannedChannels.Count() > m.stateMachine.currentChannelIndex% savedCurrentChannel = m.bsp.scannedChannels[m.stateMachine.currentChannelIndex%] endif m.ProcessScannedChannels(m.channelManager) ' reset the current channel m.stateMachine.currentChannelIndex% = 0 if type(savedCurrentChannel) = "roAssociativeArray" then for index% = 0 to m.bsp.scannedChannels.Count() - 1 scannedChannel = m.bsp.scannedChannels[index%] if scannedChannel.VirtualChannel = savedCurrentChannel.VirtualChannel then m.stateMachine.currentChannelIndex% = index% exit for endif next endif m.UpdateTunerScanPercentageComplete("100") if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else if event = 1 then m.bsp.diagnostics.PrintDebug("channel manager progress event - percentage complete =" + stri(event.GetData())) m.UpdateTunerScanPercentageComplete(stri(event.GetData())) else if event = 2 then channelDescriptor = event.GetChannelDescriptor() m.bsp.diagnostics.PrintDebug("Found channel " + channelDescriptor.VirtualChannel) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_CHANNEL_FOUND, channelDescriptor.VirtualChannel) endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub UpdateTunerScanPercentageComplete(percentageComplete$ As String) m.bsp.tunerScanPercentageComplete$ = percentageComplete$ m.bsp.UpdateTunerScanPercentageCompleteUserVariables(true) systemVariableChanged = CreateObject("roAssociativeArray") systemVariableChanged["EventType"] = "SYSTEM_VARIABLE_UPDATED" m.bsp.msgPort.PostMessage(systemVariableChanged) End Sub Sub ProcessScannedChannels(channelManager As Object) ' Get channel descriptors for all the channels that were found channelCount% = channelManager.GetChannelCount() channelDescriptors = CreateObject("roArray", channelCount%, false) if channelCount% > 0 then channelInfo = CreateObject("roAssociativeArray") for channelIndex% = 0 to channelCount% - 1 channelInfo["ChannelIndex"] = channelIndex% channelDescriptor = channelManager.CreateChannelDescriptor(channelInfo) channelDescriptors.push(channelDescriptor) next endif ' Generate XML data for the results of the scan docName$ = "BrightSignRFChannels" root = CreateObject("roXMLElement") root.SetName(docName$) root.AddAttribute("version", "1.0") rfChannels = root.AddElement("rfChannels") ' rebuild scanned channels data structure m.bsp.scannedChannels.Clear() for each channelDescriptor in channelDescriptors channelDescriptorElement = rfChannels.AddElement("rfInputChannel") virtualChannelElement = channelDescriptorElement.AddElement("virtualChannel") channelNameElement = channelDescriptorElement.AddElement("channelName") virtualChannelElement.SetBody(channelDescriptor.VirtualChannel) channelNameElement.SetBody(channelDescriptor.ChannelName) scannedChannelDescriptor = { } scannedChannelDescriptor.VirtualChannel = channelDescriptor.VirtualChannel scannedChannelDescriptor.ChannelName = channelDescriptor.ChannelName m.bsp.scannedChannels.push(scannedChannelDescriptor) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' Retrieve the XML data for the tuning data produced by system software scannedChannelData$ = channelManager.ExportToXml() ' Embed the scanned channel data in the xml as a CDATA element ' find the insert point in the xml (is there a way to write out a proper element?) index% = instr(1, xml, "") if index% > 0 then xml = mid(xml, 1, index% - 1) xml = xml + " " + chr(10) + "" endif ok = WriteAsciiFile("ScannedChannels.xml", xml) m.bsp.UpdateRFChannelCountUserVariables(true) systemVariableChanged = CreateObject("roAssociativeArray") systemVariableChanged["EventType"] = "SYSTEM_VARIABLE_UPDATED" m.bsp.msgPort.PostMessage(systemVariableChanged) End Sub Function STRFInputPlayingHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureIntraStateEventHandlerButton(m.channelUpEvent) m.ConfigureIntraStateEventHandlerButton(m.channelDownEvent) m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.stateMachine.videoPlayer.Stop() m.stateMachine.ClearImagePlane() m.stateMachine.videoPlayer.EnableSafeRegionTrimming(m.overscan) if m.stateMachine.firstTuneToChannel or m.reentryAction$ = "Retune" then tuneToOriginal = true else tuneToOriginal = false endif m.stateMachine.firstTuneToChannel = false channelDescriptor = invalid if tuneToOriginal then if m.firstScannedChannel then if m.bsp.scannedChannels.Count() > 0 then channelDescriptor = m.bsp.scannedChannels[0] else m.bsp.SendTuneFailureMessage("No scanned channels") endif else if type(m.channelDescriptor) = "roAssociativeArray" then channelDescriptor = m.channelDescriptor else if type(m.userVariable) = "roAssociativeArray" then channelSpec$ = m.userVariable.GetCurrentValue() ' first look for match with virtual channels; then try channel names if no match found for each scannedChannel in m.bsp.scannedChannels if scannedChannel.VirtualChannel = channelSpec$ then channelDescriptor = scannedChannel exit for endif next if channelDescriptor = invalid then for each scannedChannel in m.bsp.scannedChannels if scannedChannel.ChannelName = channelSpec$ then channelDescriptor = scannedChannel exit for endif next endif if channelDescriptor = invalid then m.bsp.SendTuneFailureMessage("No channel found for user variable") endif else m.bsp.SendTuneFailureMessage("No valid channel descriptor") endif ' set the current channel if not channelDescriptor = invalid then for index% = 0 to m.bsp.scannedChannels.Count() - 1 scannedChannel = m.bsp.scannedChannels[index%] if scannedChannel.VirtualChannel = channelDescriptor.VirtualChannel then m.stateMachine.currentChannelIndex% = index% exit for endif next endif else if m.bsp.scannedChannels.Count() > m.stateMachine.currentChannelIndex% then channelDescriptor = m.bsp.scannedChannels[m.stateMachine.currentChannelIndex%] else m.bsp.SendTuneFailureMessage("No scanned channel matches selected channel.") endif endif if channelDescriptor = invalid then channelToLog$ = "error" else channelToLog$ = channelDescriptor.VirtualChannel endif m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "RFIn " + channelToLog$) if type(channelDescriptor) = "roAssociativeArray" then m.bsp.TuneToChannel(m.stateMachine, channelDescriptor) endif m.LaunchTimer() m.bsp.SetTouchRegions(m) ' playback logging m.stateMachine.LogPlayStart("RFIn", channelToLog$) return "HANDLED" else if event["EventType"] = "TuneFailureEvent" then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) endif endif else if type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then if event.GetInt() = MEDIA_END then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if type(m.videoEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.videoEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif endif if type(m.channelUpEvent) = "roAssociativeArray" and m.HandleIntraStateEvent(event, m.channelUpEvent) then if m.bsp.scannedChannels.Count() = 0 then m.bsp.SendTuneFailureMessage("No scanned channels.") else ' perform channel up m.bsp.ChangeRFChannel(m.stateMachine, 1) channelDescriptor = m.bsp.scannedChannels[m.stateMachine.currentChannelIndex%] m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "RFIn " + channelDescriptor.VirtualChannel) m.bsp.TuneToChannel(m.stateMachine, channelDescriptor) endif return "HANDLED" endif if type(m.channelDownEvent) = "roAssociativeArray" and m.HandleIntraStateEvent(event, m.channelDownEvent) then if m.bsp.scannedChannels.Count() = 0 then m.bsp.SendTuneFailureMessage("No scanned channels.") else ' perform channel down m.bsp.ChangeRFChannel(m.stateMachine, -1) channelDescriptor = m.bsp.scannedChannels[m.stateMachine.currentChannelIndex%] m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "RFIn " + channelDescriptor.VirtualChannel) m.bsp.TuneToChannel(m.stateMachine, channelDescriptor) return "HANDLED" endif endif return m.MediaItemEventHandler(event, stateData) End Function Sub SendTuneFailureMessage(debugMsg As String) m.diagnostics.PrintDebug(debugMsg) tuneFailure = CreateObject("roAssociativeArray") tuneFailure["EventType"] = "TuneFailureEvent" m.msgPort.PostMessage(tuneFailure) End Sub Function TuneToChannel(zone As Object, channelDescriptor As Object) m.diagnostics.PrintDebug("Tune to the following channel descriptor:") m.diagnostics.PrintDebug("VirtualChannel " + channelDescriptor.VirtualChannel) ' m.diagnostics.PrintDebug("ChannelName " + channelDescriptor.ChannelName) ' m.diagnostics.PrintDebug("RFChannel " + stri(channelDescriptor.RFChannel)) m.rfVirtualChannel = channelDescriptor.VirtualChannel if IsString(channelDescriptor.ChannelName) then m.rfChannelName = channelDescriptor.ChannelName else m.rfChannelName = "" endif m.UpdateRFChannelUserVariables(true) systemVariableChanged = CreateObject("roAssociativeArray") systemVariableChanged["EventType"] = "SYSTEM_VARIABLE_UPDATED" m.msgPort.PostMessage(systemVariableChanged) if type(zone.syncInfo) = "roAssociativeArray" then channelDescriptor.AddReplace("SyncDomain", zone.syncInfo.SyncDomain) channelDescriptor.AddReplace("SyncId", zone.syncInfo.SyncId) channelDescriptor.AddReplace("SyncIsoTimestamp", zone.syncInfo.SyncIsoTimestamp) if m.sign.isVideoWall and m.sign.videoWallType$ = "stretched" then channelDescriptor["MultiscreenWidth"] = m.sign.videoWallNumColumns% channelDescriptor["MultiscreenHeight"] = m.sign.videoWallNumRows% channelDescriptor["MultiscreenX"] = m.sign.videoWallColumnPosition% channelDescriptor["MultiscreenY"] = m.sign.videoWallRowPosition% channelDescriptor["Mode"] = zone.viewMode% endif endif ok = zone.videoPlayer.PlayFile(channelDescriptor) if not ok then m.SendTuneFailureMessage("Error tuning in TuneToChannel") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_TUNE_FAILURE, channelDescriptor.VirtualChannel) endif End Function Function STLiveVideoPlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.stateMachine.videoPlayer.Stop() m.stateMachine.ClearImagePlane() if m.bsp.sysInfo.deviceFamily$ = "cheetah" then ' HDMI In m.stateMachine.videoPlayer.EnableSafeRegionTrimming(m.overscan) else m.stateMachine.videoPlayer.EnableSafeRegionTrimming(true) endif aa = { } aa.AddReplace("Capture", m.stateMachine.videoInput) if type(m.stateMachine.syncInfo) = "roAssociativeArray" then aa.AddReplace("SyncDomain", m.stateMachine.syncInfo.SyncDomain) aa.AddReplace("SyncId", m.stateMachine.syncInfo.SyncId) aa.AddReplace("SyncIsoTimestamp", m.stateMachine.syncInfo.SyncIsoTimestamp) if m.bsp.sign.isVideoWall and m.bsp.sign.videoWallType$ = "stretched" then aa["MultiscreenWidth"] = m.bsp.sign.videoWallNumColumns% aa["MultiscreenHeight"] = m.bsp.sign.videoWallNumRows% aa["MultiscreenX"] = m.bsp.sign.videoWallColumnPosition% aa["MultiscreenY"] = m.bsp.sign.videoWallRowPosition% aa["Mode"] = m.stateMachine.viewMode% endif endif ok = m.stateMachine.videoPlayer.PlayFile(aa) m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "liveVideo") ' playback logging m.stateMachine.LogPlayStart("liveVideo", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function STHTML5PlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.stateMachine.loadingHtmlWidget = CreateObject("roHtmlWidget", m.stateMachine.rectangle) if m.bsp.sign.htmlEnableJavascriptConsole then m.stateMachine.loadingHtmlWidget.StartInspectorServer(2999) endif if m.bsp.sign.monitorOrientation = "portrait" then m.stateMachine.loadingHtmlWidget.SetPortrait(true) endif m.stateMachine.loadingHtmlWidget.SetPort(m.bsp.msgPort) m.stateMachine.loadingHtmlWidget.EnableSecurity(not m.enableExternalData) m.stateMachine.loadingHtmlWidget.EnableMouseEvents(m.enableMouseEvents) ' if m.bsp.sign.htmlEnableAppCache then ' if m.bsp.sign.htmlAppCacheSize% > 0 then ' m.stateMachine.loadingHtmlWidget.SetAppCacheSize(m.bsp.sign.htmlAppCacheSize%) ' endif ' m.stateMachine.loadingHtmlWidget.SetAppCacheDir("") ' endif if m.contentIsLocal then syncSpec = ReadSyncSpec() if not type(syncSpec) = "roSyncSpec" stop assetCollection = syncSpec.GetAssets("download") presentationName$ = m.bsp.sign.name$ stateName$ = m.name$ prefix$ = m.prefix$ m.stateMachine.loadingHtmlWidget.MapFilesFromAssetPool(m.bsp.assetPool, assetCollection, prefix$, "/" + prefix$ + "/") m.url$ = "file:///" + prefix$ + "/" + m.filePath$ else m.url$ = m.url.GetCurrentParameterValue() endif for each customFont in m.customFonts customFontPath$ = GetPoolFilePath(m.bsp.assetPoolFiles, customFont) m.stateMachine.loadingHtmlWidget.AddFont(customFontPath$) next m.stateMachine.loadingHtmlWidget.SetUrl(m.url$) m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "html5") ' playback logging m.stateMachine.LogPlayStart("html5", m.name$) return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roHtmlWidgetEvent" then eventData = event.GetData() if type(eventData) = "roAssociativeArray" and type(eventData.reason) = "roString" then m.bsp.diagnostics.PrintDebug("reason = " + eventData.reason) if eventData.reason = "load-error" then m.bsp.diagnostics.PrintDebug("message = " + eventData.message) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_HTML5_LOAD_ERROR, eventData.message) if not m.contentIsLocal then m.htmlReloadTimer = CreateObject("roTimer") m.htmlReloadTimer.SetPort(m.bsp.msgPort) m.htmlReloadTimer.SetElapsed(30, 0) m.htmlReloadTimer.Start() endif else if eventData.reason = "load-finished" then if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif m.stateMachine.displayedHtmlWidget = m.stateMachine.loadingHtmlWidget m.stateMachine.ShowHtmlWidget() endif endif else if type(event) = "roTimerEvent" then if type(m.htmlReloadTimer) = "roTimer" and event.GetSourceIdentity() = m.htmlReloadTimer.GetIdentity() then m.bsp.diagnostics.PrintDebug("Reload Html5 widget") m.stateMachine.loadingHtmlWidget.SetURL(m.url$) return "HANDLED" else return m.MediaItemEventHandler(event, stateData) endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function IsPlayingClip() As Boolean return m.playingVideoClip or m.playingAudioClip or m.displayingImage End Function Sub ClearPlayingClip() m.playingVideoClip = false m.playingAudioClip = false m.displayingImage = false End Sub Sub ConfigureNavigationButton(navigation As Object) if type(navigation) = "roAssociativeArray" then if type(navigation.bpEvent) = "roAssociativeArray" then bpEvent = navigation.bpEvent bpEvent.configuration$ = "press" m.bsp.ConfigureBPButton(bpEvent.buttonPanelIndex%, bpEvent.buttonNumber$, bpEvent) else if type(navigation.gpioEvent) = "roAssociativeArray" then gpioEvent = navigation.gpioEvent gpioEvent.configuration$ = "press" m.bsp.ConfigureGPIOButton(gpioEvent.buttonNumber$, gpioEvent) endif endif End Sub Function STInteractiveMenuEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.playingVideoClip = false m.playingAudioClip = false m.displayingImage = false if type(m.mstimeoutEvent) = "roAssociativeArray" then m.inactivityTimer = CreateObject("roTimer") m.inactivityTimer.SetPort(m.bsp.msgPort) m.RestartInteractiveMenuInactivityTimer() endif m.imageFileTimeoutTimer = invalid m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.ConfigureNavigationButton(m.upNavigation) m.ConfigureNavigationButton(m.downNavigation) m.ConfigureNavigationButton(m.leftNavigation) m.ConfigureNavigationButton(m.rightNavigation) m.ConfigureNavigationButton(m.enterNavigation) m.ConfigureNavigationButton(m.backNavigation) m.ConfigureNavigationButton(m.nextClipNavigation) m.ConfigureNavigationButton(m.previousClipNavigation) m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.stateMachine.imagePlayer.SetDefaultTransition(0) m.currentInteractiveMenuItem = invalid if m.interactiveMenuItems.Count() > 0 then m.currentInteractiveMenuNavigationIndex% = 0 m.currentInteractiveMenuItem = m.interactiveMenuItems[0] endif m.DrawInteractiveMenu() m.bsp.SetTouchRegions(m) ' byte arrays to store stream byte input m.serialStreamInputBuffers = CreateObject("roArray", 6, true) for i% = 0 to 5 m.serialStreamInputBuffers[i%] = CreateObject("roByteArray") next ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "interactiveMenu") ' playback logging m.stateMachine.LogPlayStart("interactiveMenu", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") if type(m.inactivityTimer) = "roTimer" then m.inactivityTimer.Stop() endif if type(m.imageFileTimeoutTimer) = "roTimer" then m.imageFileTimeoutTimer.Stop() endif m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) return "HANDLED" else if event["EventType"] = "SEND_ZONE_MESSAGE" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then zoneMessage$ = event["EventParameter"] m.bsp.diagnostics.PrintDebug("ZoneMessageEvent " + zoneMessage$) retVal = m.HandleInteractiveMenuEventInput(stateData, "zoneMessage$", zoneMessage$, "zoneMessage") if retVal <> invalid then return retVal endif else if event["EventType"] = "GPIOControlDown" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then gpioNum$ = event["ButtonNumber"] m.bsp.diagnostics.PrintDebug("GPIO Press" + gpioNum$) if not m.IsPlayingClip() then if type(m.upNavigation) = "roAssociativeArray" and type(m.upNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.upNavigation.gpioEvent.buttonNumber$) and m.upNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.upNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return "HANDLED" endif if type(m.downNavigation) = "roAssociativeArray" and type(m.downNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.downNavigation.gpioEvent.buttonNumber$) and m.downNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.downNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return "HANDLED" endif if type(m.leftNavigation) = "roAssociativeArray" and type(m.leftNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.leftNavigation.gpioEvent.buttonNumber$) and m.leftNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.leftNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return "HANDLED" endif if type(m.rightNavigation) = "roAssociativeArray" and type(m.rightNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.rightNavigation.gpioEvent.buttonNumber$) and m.rightNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.rightNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return "HANDLED" endif if type(m.enterNavigation) = "roAssociativeArray" and type(m.enterNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.enterNavigation.gpioEvent.buttonNumber$) and m.enterNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return m.ExecuteInteractiveMenuEnter(stateData) endif else if type(m.backNavigation) = "roAssociativeArray" and type(m.backNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.backNavigation.gpioEvent.buttonNumber$) and m.backNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.DrawInteractiveMenu() m.ClearPlayingClip() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return "HANDLED" endif if type(m.nextClipNavigation) = "roAssociativeArray" and type(m.nextClipNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.nextClipNavigation.gpioEvent.buttonNumber$) and m.nextClipNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, 1) endif if type(m.previousClipNavigation) = "roAssociativeArray" and type(m.previousClipNavigation.gpioEvent) = "roAssociativeArray" and IsString(m.previousClipNavigation.gpioEvent.buttonNumber$) and m.previousClipNavigation.gpioEvent.buttonNumber$ = gpioNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "gpioButton", gpioNum$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, -1) endif endif else if event["EventType"] = "BPControlDown" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then bpIndex$ = event["ButtonPanelIndex"] bpIndex% = int(val(bpIndex$)) bpNum$ = event["ButtonNumber"] bpNum% = int(val(bpNum$)) m.bsp.diagnostics.PrintDebug("BP Press" + bpNum$ + " on button panel" + bpIndex$) if not m.IsPlayingClip() then if type(m.upNavigation) = "roAssociativeArray" and type(m.upNavigation.bpEvent) = "roAssociativeArray" then if m.upNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.upNavigation.bpEvent.buttonNumber$ = bpNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.upNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return "HANDLED" endif endif if type(m.downNavigation) = "roAssociativeArray" and type(m.downNavigation.bpEvent) = "roAssociativeArray" then if m.downNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.downNavigation.bpEvent.buttonNumber$ = bpNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.downNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return "HANDLED" endif endif if type(m.leftNavigation) = "roAssociativeArray" and type(m.leftNavigation.bpEvent) = "roAssociativeArray" then if m.leftNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.leftNavigation.bpEvent.buttonNumber$ = bpNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.leftNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return "HANDLED" endif endif if type(m.rightNavigation) = "roAssociativeArray" and type(m.rightNavigation.bpEvent) = "roAssociativeArray" then if m.rightNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.rightNavigation.bpEvent.buttonNumber$ = bpNum$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.rightNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return "HANDLED" endif endif if type(m.enterNavigation) = "roAssociativeArray" and type(m.enterNavigation.bpEvent) = "roAssociativeArray" then if m.enterNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.enterNavigation.bpEvent.buttonNumber$ = bpNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return m.ExecuteInteractiveMenuEnter(stateData) endif endif else if type(m.backNavigation) = "roAssociativeArray" and type(m.backNavigation.bpEvent) = "roAssociativeArray" then if m.backNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.backNavigation.bpEvent.buttonNumber$ = bpNum$ then m.DrawInteractiveMenu() m.ClearPlayingClip() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return "HANDLED" endif endif if type(m.nextClipNavigation) = "roAssociativeArray" and type(m.nextClipNavigation.bpEvent) = "roAssociativeArray" then if m.nextClipNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.nextClipNavigation.bpEvent.buttonNumber$ = bpNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, 1) endif endif if type(m.previousClipNavigation) = "roAssociativeArray" and type(m.previousClipNavigation.bpEvent) = "roAssociativeArray" then if m.previousClipNavigation.bpEvent.buttonPanelIndex% = bpIndex% and m.previousClipNavigation.bpEvent.buttonNumber$ = bpNum$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "bpDown", bpIndex$ + " " + bpNum$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, -1) endif endif endif endif endif else if type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() then m.bsp.diagnostics.PrintDebug("Video Event" + stri(event.GetInt())) if event.GetInt() = MEDIA_END and m.playingVideoClip then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif m.playingVideoClip = false m.DrawInteractiveMenu() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") else if event.GetInt() = MEDIA_END and m.playingAudioClip then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif m.playingAudioClip = false m.DrawInteractiveMenu() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") else m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "0") endif return "HANDLED" ' this should no longer trigger - I think an interactive menu requires a VideoImages zone which doesn't include an audio player else if type(event) = "roAudioEvent" then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) if event.GetInt() = MEDIA_END and m.playingAudioClip then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif m.playingAudioClip = false m.DrawInteractiveMenu() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") else m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "0") endif return "HANDLED" else if type(event) = "roTimerEvent" then if type(m.inactivityTimer) = "roTimer" and event.GetSourceIdentity() = m.inactivityTimer.GetIdentity() then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timer", "", "1") return m.ExecuteTransition(m.mstimeoutEvent, stateData, "") endif if type(m.imageFileTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.imageFileTimeoutTimer.GetIdentity() then if m.displayingImage then m.displayingImage = false m.DrawInteractiveMenu() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timer", "", "1") else m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timer", "", "0") endif return "HANDLED" endif else if type(event) = "roStreamByteEvent" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("Serial Byte Event " + str(event.GetInt())) serialByte% = event.GetInt() port$ = event.GetUserData() port% = int(val(port$)) serialStreamInput = m.serialStreamInputBuffers[port%] while serialStreamInput.Count() >= 64 serialStreamInput.Shift() end while serialStreamInput.push(serialByte%) if not m.IsPlayingClip() then status$ = m.ConsumeSerialByteInput(stateData, m.upNavigation, m.currentInteractiveMenuItem.upNavigationIndex%, serialStreamInput, port$, "navigate") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.downNavigation, m.currentInteractiveMenuItem.downNavigationIndex%, serialStreamInput, port$, "navigate") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.leftNavigation, m.currentInteractiveMenuItem.leftNavigationIndex%, serialStreamInput, port$, "navigate") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.rightNavigation, m.currentInteractiveMenuItem.rightNavigationIndex%, serialStreamInput, port$, "navigate") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.enterNavigation, -1, serialStreamInput, port$, "enter") if status$ <> "" return status$ else status$ = m.ConsumeSerialByteInput(stateData, m.backNavigation, -1, serialStreamInput, port$, "back") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.nextClipNavigation, -1, serialStreamInput, port$, "nextClip") if status$ <> "" return status$ status$ = m.ConsumeSerialByteInput(stateData, m.previousClipNavigation, -1, serialStreamInput, port$, "previousClip") if status$ <> "" return status$ endif else if type(event) = "roStreamLineEvent" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("Serial Line Event " + event.GetString()) port$ = event.GetUserData() serialEvent$ = event.GetString() if not m.IsPlayingClip() then if type(m.upNavigation) = "roAssociativeArray" and type(m.upNavigation.serialEvent) = "roAssociativeArray" then if m.upNavigation.serialEvent.protocol$ <> "Binary" and m.upNavigation.serialEvent.port$ = port$ and m.upNavigation.serialEvent.serial$ = serialEvent$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.upNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return "HANDLED" endif endif if type(m.downNavigation) = "roAssociativeArray" and type(m.downNavigation.serialEvent) = "roAssociativeArray" then if m.downNavigation.serialEvent.protocol$ <> "Binary" and m.downNavigation.serialEvent.port$ = port$ and m.downNavigation.serialEvent.serial$ = serialEvent$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.downNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return "HANDLED" endif endif if type(m.leftNavigation) = "roAssociativeArray" and type(m.leftNavigation.serialEvent) = "roAssociativeArray" then if m.leftNavigation.serialEvent.protocol$ <> "Binary" and m.leftNavigation.serialEvent.port$ = port$ and m.leftNavigation.serialEvent.serial$ = serialEvent$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.leftNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return "HANDLED" endif endif if type(m.rightNavigation) = "roAssociativeArray" and type(m.rightNavigation.serialEvent) = "roAssociativeArray" then if m.rightNavigation.serialEvent.protocol$ <> "Binary" and m.rightNavigation.serialEvent.port$ = port$ and m.rightNavigation.serialEvent.serial$ = serialEvent$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.rightNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return "HANDLED" endif endif if type(m.enterNavigation) = "roAssociativeArray" and type(m.enterNavigation.serialEvent) = "roAssociativeArray" then if m.enterNavigation.serialEvent.protocol$ <> "Binary" and m.enterNavigation.serialEvent.port$ = port$ and m.enterNavigation.serialEvent.serial$ = serialEvent$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return m.ExecuteInteractiveMenuEnter(stateData) endif endif else if type(m.backNavigation) = "roAssociativeArray" and type(m.backNavigation.serialEvent) = "roAssociativeArray" then if m.backNavigation.serialEvent.protocol$ <> "Binary" and m.backNavigation.serialEvent.port$ = port$ and m.backNavigation.serialEvent.serial$ = serialEvent$ then m.DrawInteractiveMenu() m.ClearPlayingClip() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return "HANDLED" endif endif if type(m.nextClipNavigation) = "roAssociativeArray" and type(m.nextClipNavigation.serialEvent) = "roAssociativeArray" then if m.nextClipNavigation.serialEvent.protocol$ <> "Binary" and m.nextClipNavigation.serialEvent.port$ = port$ and m.nextClipNavigation.serialEvent.serial$ = serialEvent$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, 1) endif endif if type(m.previousClipNavigation) = "roAssociativeArray" and type(m.previousClipNavigation.serialEvent) = "roAssociativeArray" then if m.previousClipNavigation.serialEvent.protocol$ <> "Binary" and m.previousClipNavigation.serialEvent.port$ = port$ and m.previousClipNavigation.serialEvent.serial$ = serialEvent$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", port$ + " " + serialEvent$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, -1) endif endif endif else if type(event) = "roIRRemotePress" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then remoteEvent% = event.GetInt() remoteEvent$ = ConvertToRemoteCommand(remoteEvent%) retVal = m.HandleInteractiveMenuEventInput(stateData, "remoteEvent$", remoteEvent$, "remote") if retVal <> invalid then return retVal endif else if type(event) = "roKeyboardPress" and type(m.currentInteractiveMenuItem) = "roAssociativeArray" then keyboardChar$ = chr(event.GetInt()) ' is not supported here - it doesn't make sense. ' if keyboard input is non printable character, convert it to the special code keyboardCode$ = m.bsp.GetNonPrintableKeyboardCode(event.GetInt()) if keyboardCode$ <> "" then keyboardChar$ = keyboardCode$ endif retVal = m.HandleInteractiveMenuEventInput(stateData, "keyboardChar$", keyboardChar$, "keyboard") if retVal <> invalid then return retVal endif endif return m.MediaItemEventHandler(event, stateData) End Function Function HandleInteractiveMenuEventInput(stateData As Object, navigationUserEvent As Object, eventData$ As String, eventType$ As String) As Object if not m.IsPlayingClip() then if type(m.upNavigation) = "roAssociativeArray" and IsString(m.upNavigation.Lookup(navigationUserEvent)) and m.upNavigation.Lookup(navigationUserEvent) = eventData$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.upNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return "HANDLED" endif if type(m.downNavigation) = "roAssociativeArray" and IsString(m.downNavigation.Lookup(navigationUserEvent)) and m.downNavigation.Lookup(navigationUserEvent) = eventData$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.downNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return "HANDLED" endif if type(m.leftNavigation) = "roAssociativeArray" and IsString(m.leftNavigation.Lookup(navigationUserEvent)) and m.leftNavigation.Lookup(navigationUserEvent) = eventData$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.leftNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return "HANDLED" endif if type(m.rightNavigation) = "roAssociativeArray" and IsString(m.rightNavigation.Lookup(navigationUserEvent)) and m.rightNavigation.Lookup(navigationUserEvent) = eventData$ then m.NavigateToMenuItem(m.currentInteractiveMenuItem.rightNavigationIndex%) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return "HANDLED" endif if type(m.enterNavigation) = "roAssociativeArray" and IsString(m.enterNavigation.Lookup(navigationUserEvent)) and m.enterNavigation.Lookup(navigationUserEvent) = eventData$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return m.ExecuteInteractiveMenuEnter(stateData) endif else if type(m.backNavigation) = "roAssociativeArray" and IsString(m.backNavigation.Lookup(navigationUserEvent)) and m.backNavigation.Lookup(navigationUserEvent) = eventData$ then m.DrawInteractiveMenu() m.ClearPlayingClip() m.RestartInteractiveMenuInactivityTimer() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return "HANDLED" endif if type(m.nextClipNavigation) = "roAssociativeArray" and IsString(m.nextClipNavigation.Lookup(navigationUserEvent)) and m.nextClipNavigation.Lookup(navigationUserEvent) = eventData$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, 1) endif if type(m.previousClipNavigation) = "roAssociativeArray" and IsString(m.previousClipNavigation.Lookup(navigationUserEvent)) and m.previousClipNavigation.Lookup(navigationUserEvent) = eventData$ then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, eventType$, eventData$, "1") return m.NextPrevInteractiveMenuLaunchMedia(stateData, -1) endif endif return invalid End Function Function ConsumeSerialByteInput(stateData As Object, navigation As Object, navigationIndex% As integer, serialStreamInput As Object, port$ As String, command$ As String) As String if type(navigation) = "roAssociativeArray" and type(navigation.serialEvent) = "roAssociativeArray" then if navigation.serialEvent.protocol$ = "Binary" and navigation.serialEvent.port$ = port$ then if ByteArraysMatch(serialStreamInput, navigation.serialEvent.inputSpec) then serialStreamInput.Clear() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serialBytes", navigation.serialEvent.asciiSpec, "1") if command$ = "navigate" then m.NavigateToMenuItem(navigationIndex%) else if command$ = "enter" then return m.ExecuteInteractiveMenuEnter(stateData) else if command$ = "back" then m.DrawInteractiveMenu() m.ClearPlayingClip() m.RestartInteractiveMenuInactivityTimer() else if command$ = "nextClip" then return m.NextPrevInteractiveMenuLaunchMedia(stateData, 1) else if command$ = "previousClip" then return m.NextPrevInteractiveMenuLaunchMedia(stateData, -1) endif return "HANDLED" endif endif endif return "" End Function Function ExecuteInteractiveMenuEnter(stateData As Object) As String if type(m.inactivityTimer) = "roTimer" then m.inactivityTimer.Stop() endif ' execute commands if type(m.currentInteractiveMenuItem.enterCmds) = "roArray" then for each cmd in m.currentInteractiveMenuItem.enterCmds if cmd.name$ = "switchPresentation" then presentationName$ = cmd.parameters["presentationName"].GetCurrentParameterValue() switchToNewPresentation = m.bsp.ExecuteSwitchPresentationCommand(presentationName$) if switchToNewPresentation then return "HANDLED" endif endif m.bsp.ExecuteCmd(m.stateMachine, cmd.name$, cmd.parameters) next endif launchedPlayback = false if m.currentInteractiveMenuItem.targetType$ = "mediaFile" then if IsString(m.currentInteractiveMenuItem.targetVideoFile$) and m.currentInteractiveMenuItem.targetVideoFile$ <> "" then m.LaunchInteractiveMenuVideoClip(m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.targetVideoFileUserVariable) launchedPlayback = true else if IsString(m.currentInteractiveMenuItem.targetAudioFile$) and m.currentInteractiveMenuItem.targetAudioFile$ <> "" then m.LaunchInteractiveMenuAudioClip(m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.targetAudioFileUserVariable) launchedPlayback = true else if IsString(m.currentInteractiveMenuItem.targetImageFile$) and m.currentInteractiveMenuItem.targetImageFile$ <> "" then file$ = m.currentInteractiveMenuItem.targetImageFile$ m.DisplayInteractiveMenuImage(file$, m.currentInteractiveMenuItem.targetImageFileUserVariable, m.currentInteractiveMenuItem.targetImageFileTimeout%, m.currentInteractiveMenuItem.targetImageFileUseImageBuffer) launchedPlayback = true endif else if m.currentInteractiveMenuItem.targetType$ = "mediaState" then stateData.nextState = m.stateMachine.stateTable[m.currentInteractiveMenuItem.targetMediaState$] if stateData.nextState.type$ = "superState" and stateData.nextState.firstState <> invalid then targetMediaState$ = stateData.nextState.firstState.id$ stateData.nextState = m.stateMachine.stateTable[targetMediaState$] endif m.UpdatePreviousCurrentStateNames() return "TRANSITION" else if m.currentInteractiveMenuItem.targetType$ = "previousState" then stateData.nextState = m.stateMachine.stateTable[m.stateMachine.previousStateName$] m.UpdatePreviousCurrentStateNames() return "TRANSITION" else ' currentState endif if not launchedPlayback and type(m.inactivityTimer) = "roTimer" then m.RestartInteractiveMenuInactivityTimer() endif return "HANDLED" End Function Function NextPrevInteractiveMenuLaunchMedia(stateData As Object, incrementValue% As Integer) As String while true m.currentInteractiveMenuNavigationIndex% = m.currentInteractiveMenuNavigationIndex% + incrementValue% if incrementValue% > 0 then if m.currentInteractiveMenuNavigationIndex% >= m.interactiveMenuItems.Count() then m.currentInteractiveMenuNavigationIndex% = 0 endif else if m.currentInteractiveMenuNavigationIndex% < 0 then m.currentInteractiveMenuNavigationIndex% = m.interactiveMenuItems.Count() - 1 endif endif m.currentInteractiveMenuItem = m.interactiveMenuItems[m.currentInteractiveMenuNavigationIndex%] ' execute commands if type(m.currentInteractiveMenuItem.enterCmds) = "roArray" then for each cmd in m.currentInteractiveMenuItem.enterCmds m.bsp.ExecuteCmd(m.stateMachine, cmd.name$, cmd.parameters) next endif if m.currentInteractiveMenuItem.targetType$ = "mediaFile" then if IsString(m.currentInteractiveMenuItem.targetVideoFile$) and m.currentInteractiveMenuItem.targetVideoFile$ <> "" then m.LaunchInteractiveMenuVideoClip(m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.targetVideoFileUserVariable) return "HANDLED" else if IsString(m.currentInteractiveMenuItem.targetAudioFile$) and m.currentInteractiveMenuItem.targetAudioFile$ <> "" then m.LaunchInteractiveMenuAudioClip(m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.targetAudioFileUserVariable) return "HANDLED" else if IsString(m.currentInteractiveMenuItem.targetImageFile$) and m.currentInteractiveMenuItem.targetImageFile$ <> "" then m.DisplayInteractiveMenuImage(m.currentInteractiveMenuItem.targetImageFile$, m.currentInteractiveMenuItem.targetImageFileUserVariable, m.currentInteractiveMenuItem.targetImageFileTimeout%, m.currentInteractiveMenuItem.targetImageFileUseImageBuffer) return "HANDLED" endif else if m.currentInteractiveMenuItem.targetType$ = "mediaState" then stateData.nextState = m.stateMachine.stateTable[m.currentInteractiveMenuItem.targetMediaState$] m.UpdatePreviousCurrentStateNames() return "TRANSITION" else if m.currentInteractiveMenuItem.targetType$ = "previousState" then stateData.nextState = m.stateMachine.stateTable[m.stateMachine.previousStateName$] m.UpdatePreviousCurrentStateNames() return "TRANSITION" else ' currentState endif end while End Function Sub LaunchInteractiveMenuVideoClip(interactiveMenuItem As Object, userVariable As Object) m.stateMachine.videoPlayer.SetLoopMode(0) m.stateMachine.videoPlayer.EnableSafeRegionTrimming(false) m.stateMachine.videoPlayer.StopClear() ' m.stateMachine.audioPlayer.Stop() file$ = interactiveMenuItem.targetVideoFile$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) aa = { } aa.AddReplace("Filename", filePath$) if type(interactiveMenuItem.probeData) = "roString" then m.stateMachine.bsp.diagnostics.PrintDebug("LaunchInteractiveMenuVideoClip: probeData = " + interactiveMenuItem.probeData) aa.AddReplace("ProbeString", interactiveMenuItem.probeData) endif ok = m.stateMachine.videoPlayer.PlayFile(aa) if ok = 0 then stop m.stateMachine.ClearImagePlane() if type(userVariable) = "roAssociativeArray" then userVariable.Increment() endif m.playingVideoClip = true m.playingAudioClip = false m.displayingImage = false End Sub Sub LaunchInteractiveMenuAudioClip(interactiveMenuItem As Object, userVariable As Object) if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer m.stateMachine.audioPlayer.SetLoopMode(0) m.stateMachine.audioPlayer.Stop() else player = m.stateMachine.videoPlayer m.stateMachine.videoPlayer.StopClear() endif file$ = interactiveMenuItem.targetAudioFile$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) aa = { } aa.AddReplace("Filename", filePath$) if type(interactiveMenuItem.probeData) = "roString" then m.stateMachine.bsp.diagnostics.PrintDebug("LaunchInteractiveMenuAudioClip: probeData = " + interactiveMenuItem.probeData) aa.AddReplace("ProbeString", interactiveMenuItem.probeData) endif ok = player.PlayFile(aa) if ok = 0 then stop m.stateMachine.ClearImagePlane() if type(userVariable) = "roAssociativeArray" then userVariable.Increment() endif m.playingAudioClip = true m.playingVideoClip = false m.displayingImage = false End Sub Sub DisplayInteractiveMenuImage(file$ As String, userVariable As Object, timeout% As Integer, useImageBuffer As Boolean) ' m.stateMachine.audioPlayer.Stop() m.stateMachine.videoPlayer.StopClear() filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) if useImageBuffer then m.bsp.diagnostics.PrintDebug("Use imageBuffer for " + file$ + " in DisplayInteractiveMenuImage.") imageBuffer = m.bsp.imageBuffers.Lookup(filePath$) m.stateMachine.imagePlayer.DisplayBuffer(imageBuffer, 0, 0) else ok = m.stateMachine.imagePlayer.PreloadFile(filePath$) ok = m.stateMachine.imagePlayer.DisplayPreload() endif m.stateMachine.ShowImageWidget() m.playingVideoClip = false m.playingAudioClip = false m.displayingImage = true if type(m.imageFileTimeoutTimer) = "roTimer" then m.imageFileTimeoutTimer.Stop() else m.imageFileTimeoutTimer = CreateObject("roTimer") m.imageFileTimeoutTimer.SetPort(m.bsp.msgPort) endif m.imageFileTimeoutTimer.SetElapsed(timeout%, 0) m.imageFileTimeoutTimer.Start() if type(userVariable) = "roAssociativeArray" then userVariable.Increment() endif End Sub Sub DrawInteractiveMenu() ' execute entry commands any time the interactive menu is displayed m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) ' display background image if it exists if IsString(m.backgroundImage$) then file$ = m.backgroundImage$ if file$ <> "" then filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) m.stateMachine.videoPlayer.StopClear() if m.backgroundImageUseImageBuffer and type(m.bsp.imageBuffers) = "roAssociativeArray" and m.bsp.imageBuffers.DoesExist(filePath$) then m.bsp.diagnostics.PrintDebug("Use imageBuffer for " + file$ + " in DrawInteractiveMenu.") imageBuffer = m.bsp.imageBuffers.Lookup(filePath$) m.stateMachine.imagePlayer.DisplayBuffer(imageBuffer, 0, 0) else m.bsp.diagnostics.PrintDebug("DisplayFile in STInteractiveMenuEventHandler: " + file$) ok = m.stateMachine.imagePlayer.PreloadFile(filePath$) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error preloading file in DrawInteractiveMenu: " + file$ + ", " + filePath$) else ok = m.stateMachine.imagePlayer.DisplayPreload() if ok = 0 then m.bsp.diagnostics.PrintDebug("Error in DisplayPreload in DrawInteractiveMenu: " + file$ + ", " + filePath$) endif endif endif m.stateMachine.ShowImageWidget() else m.stateMachine.ClearImagePlane() endif endif ' draw backgrounds for each interactiveMenuItem in m.interactiveMenuItems m.DisplayNavigationOverlay(interactiveMenuItem.unselectedImage$, interactiveMenuItem, interactiveMenuItem.unselectedImageUseImageBuffer) next ' draw foreground for first menu item if type(m.currentInteractiveMenuItem) = "roAssociativeArray" then m.DisplayNavigationOverlay(m.currentInteractiveMenuItem.selectedImage$, m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.selectedImageUseImageBuffer) endif if IsString(m.backgroundImage$) and m.backgroundImage$ <> "" and type(m.backgroundImageUserVariable) = "roAssociativeArray" then m.backgroundImageUserVariable.Increment() endif End Sub Sub NavigateToMenuItem(navigationIndex% As Integer) m.RestartInteractiveMenuInactivityTimer() if navigationIndex% >= 0 then m.DisplayNavigationOverlay(m.currentInteractiveMenuItem.unselectedImage$, m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.unselectedImageUseImageBuffer) m.currentInteractiveMenuNavigationIndex% = navigationIndex% m.currentInteractiveMenuItem = m.interactiveMenuItems[navigationIndex%] m.DisplayNavigationOverlay(m.currentInteractiveMenuItem.selectedImage$, m.currentInteractiveMenuItem, m.currentInteractiveMenuItem.selectedImageUseImageBuffer) endif End Sub Sub RestartInteractiveMenuInactivityTimer() if type(m.inactivityTimer) = "roTimer" then m.inactivityTimer.Stop() m.inactivityTimer.SetElapsed(0, m.mstimeoutValue%) m.inactivityTimer.Start() endif End Sub Sub DisplayNavigationOverlay(fileName$ As String, interactiveMenuItem As Object, useImageBuffer As Boolean) if IsString(fileName$) and fileName$ <> "" then filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, fileName$) if useImageBuffer then m.bsp.diagnostics.PrintDebug("Use imageBuffer for " + fileName$ + " in DisplayNavigationOverlay.") imageBuffer = m.bsp.imageBuffers.Lookup(filePath$) m.stateMachine.imagePlayer.DisplayBuffer(imageBuffer, interactiveMenuItem.x%,interactiveMenuItem.y%) else m.bsp.diagnostics.PrintDebug("Overlay image in DisplayNavigationOverlay: " + fileName$) ok = m.stateMachine.imagePlayer.OverlayImage(filePath$, interactiveMenuItem.x%, interactiveMenuItem.y%) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error in OverlayImage in DisplayNavigationOverlay: " + fileName$ + ", " + filePath$) endif endif m.stateMachine.ShowImageWidget() endif End Sub Sub ScaleBackgroundImageToFit( backgroundImage As Object ) xScale = m.backgroundImageWidth% / m.stateMachine.width% yScale = m.backgroundImageHeight% / m.stateMachine.height% if xScale > yScale then x% = 0 y% = (m.stateMachine.height% - (m.backgroundImageHeight% / xScale)) / 2 width% = m.backgroundImageWidth% / xScale height% = m.backgroundImageHeight% / xScale else x% = (m.stateMachine.width% - (m.backgroundImageWidth% / yScale)) / 2 y% = 0 width% = m.backgroundImageWidth% / yScale height% = m.backgroundImageHeight% / yScale endif backgroundImage["targetRect"] = { x: x%, y: y%, w: width%, h: height% } End Sub Sub SetBackgroundImageSizeLocation( backgroundImage As Object ) if m.stateMachine.imageMode% = 0 ' center image if m.backgroundImageWidth% > m.stateMachine.width% or m.backgroundImageHeight% > m.stateMachine.height% then m.ScaleBackgroundImageToFit(backgroundImage) else x% = (m.stateMachine.width% - m.backgroundImageWidth%) / 2 y% = (m.stateMachine.height% - m.backgroundImageHeight%) / 2 backgroundImage["targetRect"] = { x: x%, y: y%, w: m.backgroundImageWidth%, h: m.backgroundImageHeight% } endif else if m.stateMachine.imageMode% = 1 ' scale to fit m.ScaleBackgroundImageToFit(backgroundImage) else if m.stateMachine.imageMode% = 2 ' scale to fill and crop m.ScaleBackgroundImageToFit(backgroundImage) else if m.stateMachine.imageMode% ' scale to fill backgroundImage["targetRect"] = { x: 0, y: 0, w: m.stateMachine.width%, h: m.stateMachine.height% } endif End Sub Function TemplateUsesAnyUserVariable() As Boolean for each templateItem in m.templateItems if templateItem.type$ = "userVariableTemplateItem" then return true endif next End Function Function TemplateUsesUserVariable(userVariable As Object) As Boolean for each templateItem in m.templateItems if templateItem.type$ = "userVariableTemplateItem" then if type(templateItem.userVariable) = "roAssociativeArray" then if templateItem.userVariable.name$ = userVariable.name$ then return true endif endif endif next End Function Function TemplateUsesSystemVariable() As Boolean for each templateItem in m.templateItems if templateItem.type$ = "systemVariableTextTemplateItem" return true endif next End Function Sub BuildBaseTemplateItem(templateItem As Object, content As Object) content["targetRect"] = { x: templateItem.x%, y: templateItem.y%, w: templateItem.width%, h: templateItem.height% } End Sub Sub BuildTextTemplateItem(templateItem As Object, content As Object) BuildBaseTemplateItem(templateItem, content) textAttrs = { } textAttrs.color = templateItem.foregroundTextColor$ textAttrs.fontSize = templateItem.fontSize% if templateItem.font$ <> "System" then textAttrs.fontFile = GetPoolFilePath(m.bsp.assetPoolFiles, templateItem.font$) endif textAttrs.vAlign = "Top" textAttrs.hAlign = templateItem.alignment$ textAttrs.rotation = templateItem.rotation$ content.textAttrs = textAttrs End Sub Sub RedisplayTemplateItems() m.BuildTemplateItems() m.stateMachine.canvasWidget.EnableAutoRedraw(0) numLayers% = m.stateMachine.templateObjectsByLayer.Count() for i% = 0 to numLayers% - 1 if type(m.stateMachine.templateObjectsByLayer[i%]) = "roArray" then m.stateMachine.canvasWidget.SetLayer(m.stateMachine.templateObjectsByLayer[i%], i% + 1) else m.stateMachine.canvasWidget.ClearLayer(i% + 1) endif next m.stateMachine.canvasWidget.EnableAutoRedraw(1) End Sub Sub BuildTemplateItems() m.stateMachine.templateObjectsByLayer = CreateObject("roArray", 1, true) for each templateItem in m.templateItems text = invalid image = invalid backgroundLayer% = (templateItem.layer% - 1) * 2 + 1 contentLayer% = backgroundLayer% + 1 if templateItem.type$ = "constantTextTemplateItem" then text = { } text["text"] = templateItem.textString$ m.BuildTextTemplateItem(templateItem, text) else if templateItem.type$ = "systemVariableTextTemplateItem" then text = { } if templateItem.systemVariableType$ = "SerialNumber" then text["text"] = m.bsp.sysInfo.deviceUniqueID$ else if templateItem.systemVariableType$ = "IPAddressWired" then text["text"] = m.bsp.sysInfo.ipAddressWired$ else if templateItem.systemVariableType$ = "IPAddressWireless" then text["text"] = m.bsp.sysInfo.ipAddressWireless$ else if templateItem.systemVariableType$ = "FirmwareVersion" then text["text"] = m.bsp.sysInfo.deviceFWVersion$ else if templateItem.systemVariableType$ = "ScriptVersion" then text["text"] = m.bsp.sysInfo.autorunVersion$ else if templateItem.systemVariableType$ = "RFChannelCount" then text["text"] = StripLeadingSpaces(stri(m.bsp.scannedChannels.Count())) else if templateItem.systemVariableType$ = "RFChannelName" then text["text"] = StripLeadingSpaces(m.bsp.rfChannelName) else if templateItem.systemVariableType$ = "RFVirtualChannel" then text["text"] = StripLeadingSpaces(m.bsp.rfVirtualChannel) else if templateItem.systemVariableType$ = "TunerScanPercentageComplete" then text["text"] = m.bsp.tunerScanPercentageComplete$ else if templateItem.systemVariableType$ = "EdidMonitorSerialNumber" then text["text"] = m.bsp.sysInfo.edidMonitorSerialNumber$ else if templateItem.systemVariableType$ = "EdidYearOfManufacture" then text["text"] = m.bsp.sysInfo.edidYearOfManufacture$ else if templateItem.systemVariableType$ = "EdidMonitorName" then text["text"] = m.bsp.sysInfo.edidMonitorName$ else if templateItem.systemVariableType$ = "EdidManufacturer" then text["text"] = m.bsp.sysInfo.edidManufacturer$ else if templateItem.systemVariableType$ = "EdidUnspecifiedText" then text["text"] = m.bsp.sysInfo.edidUnspecifiedText$ else if templateItem.systemVariableType$ = "EdidSerialNumber" then text["text"] = m.bsp.sysInfo.edidSerialNumber$ else if templateItem.systemVariableType$ = "EdidManufacturerProductCode" then text["text"] = m.bsp.sysInfo.edidManufacturerProductCode$ else if templateItem.systemVariableType$ = "EdidWeekOfManufacture" then text["text"] = m.bsp.sysInfo.edidWeekOfManufacture$ else if templateItem.systemVariableType$ = "ActivePresentation" then if IsString(m.bsp.activePresentation$) then text["text"] = m.bsp.activePresentation$ else text["text"] = "" endif endif m.BuildTextTemplateItem(templateItem, text) else if templateItem.type$ = "mediaCounterTemplateItem" or templateItem.type$ = "userVariableTemplateItem" then if type(templateItem.userVariable) = "roAssociativeArray" then text = { } text["text"] = templateItem.userVariable.GetCurrentValue() m.BuildTextTemplateItem(templateItem, text) endif else if templateItem.type$ = "indexedLiveTextDataEntryTemplateItem" or templateItem.type$ = "titledLiveTextDataEntryTemplateItem" then liveDataFeed = templateItem.liveDataFeed if m.liveDataFeeds.DoesExist(liveDataFeed.name$) then if templateItem.type$ = "indexedLiveTextDataEntryTemplateItem" then indexStr$ = templateItem.index.GetCurrentParameterValue() index% = int(val(indexStr$)) if index% > 0 then index% = index% - 1 endif if type(liveDataFeed.articles) = "roArray" then if index% <= (liveDataFeed.articles.count() - 1) then textValue$ = liveDataFeed.articles[index%] text = { } text["text"] = textValue$ m.BuildTextTemplateItem(templateItem, text) endif endif else title$ = templateItem.title.GetCurrentParameterValue() if type(liveDataFeed.articlesByTitle) = "roAssociativeArray" then if liveDataFeed.articlesByTitle.DoesExist(title$) then textValue$ = liveDataFeed.articlesByTitle.Lookup(title$) text = { } text["text"] = textValue$ m.BuildTextTemplateItem(templateItem, text) endif endif endif endif else if templateItem.type$ = "imageTemplateItem" then image = { } image["filename"] = GetPoolFilePath(m.bsp.assetPoolFiles, templateItem.fileName$) image["CompositionMode"] = "source_over" ' TemplateToDo - do the images care about the stretch/crop mode? ' m.SetBackgroundImageSizeLocation(backgroundImage) BuildBaseTemplateItem(templateItem, image) else if templateItem.type$ = "mrssTextTemplateItem" then if type(templateItem.textString$) <> "Invalid" then text = { } text["text"] = templateItem.textString$ m.BuildTextTemplateItem(templateItem, text) endif else if templateItem.type$ = "mrssImageTemplateItem" then if type(templateItem.fileName$) <> "Invalid" then image = { } image["filename"] = templateItem.fileName$ image["CompositionMode"] = "source-over" ' TemplateToDo - do the images care about the stretch/crop mode? ' m.SetBackgroundImageSizeLocation(backgroundImage) BuildBaseTemplateItem(templateItem, image) endif endif m.BuildTemplateItem(text, image, templateItem) next ' now add any simple rss items if type(m.simpleRSSTextTemplateItems) = "roAssociativeArray" then for each simpleRSSId in m.simpleRSSTextTemplateItems simpleRSS = m.simpleRSSTextTemplateItems.Lookup(simpleRSSId) if type(simpleRSS) = "roAssociativeArray" then liveDataFeed = simpleRSS.rssLiveDataFeeds[simpleRSS.currentLiveDataFeedIndex%] if m.liveDataFeeds.DoesExist(liveDataFeed.name$) then if type(liveDataFeed.articles) = "roArray" then if simpleRSS.currentIndex% >= liveDataFeed.articles.count() then simpleRSS.currentIndex% = 0 endif index% = simpleRSS.currentIndex% ' remove the next conditional - it's not needed if index% <= (liveDataFeed.articles.count() - 1) then for each templateItem in simpleRSS.items if templateItem.elementName$ = "title" then textValue$ = liveDataFeed.articleTitles[index%] else textValue$ = liveDataFeed.articles[index%] endif text = { } text["text"] = textValue$ m.BuildTextTemplateItem(templateItem, text) m.BuildTemplateItem(text, image, templateItem) next ' first time display - start timer to display next item if type(simpleRSS.rssItemTimer) <> "roTimer" then simpleRSS.rssItemTimer = CreateObject("roTimer") simpleRSS.rssItemTimer.SetPort(m.stateMachine.msgPort) simpleRSS.rssItemTimer.SetElapsed(simpleRSS.displayTime%, 0) simpleRSS.rssItemTimer.Start() endif endif endif endif endif next endif End Sub Sub BuildTemplateItem(text As Object, image As Object, templateItem As Object) if type(text) = "roAssociativeArray" or type(image) = "roAssociativeArray" then backgroundLayer% = (templateItem.layer% - 1) * 2 + 1 contentLayer% = backgroundLayer% + 1 if type(m.stateMachine.templateObjectsByLayer[contentLayer%]) <> "roArray" then m.stateMachine.templateObjectsByLayer[contentLayer%] = CreateObject("roArray", 1, true) endif if type(text) = "roAssociativeArray" then m.stateMachine.templateObjectsByLayer[contentLayer%].push(text) if templateItem.backgroundColorSpecified then backgroundColor = { } backgroundColor["color"] = templateItem.backgroundTextColor$ backgroundColor["targetRect"] = { x: templateItem.x%, y: templateItem.y%, w: templateItem.width%, h: templateItem.height% } if type(m.stateMachine.templateObjectsByLayer[backgroundLayer%]) <> "roArray" then m.stateMachine.templateObjectsByLayer[backgroundLayer%] = CreateObject("roArray", 1, true) endif m.stateMachine.templateObjectsByLayer[backgroundLayer%].push(backgroundColor) endif else m.stateMachine.templateObjectsByLayer[contentLayer%].push(image) endif endif End Sub Sub SetupTemplateMRSS() if type(m.mrssTitleTemplateItem) = "roAssociativeArray" then m.mrssTitleTemplateItem.textString$ = invalid endif if type(m.mrssDescriptionTemplateItem) = "roAssociativeArray" then m.mrssDescriptionTemplateItem.textString$ = invalid endif if type(m.mrssImageTemplateItem) = "roAssociativeArray" then m.mrssImageTemplateItem.fileName$ = invalid endif End Sub Function STTemplatePlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") if m.mrssActive then m.SetupTemplateMRSS() m.currentFeed = invalid m.pendingFeed = invalid m.mrssItemTimer = CreateObject("roTimer") m.mrssItemTimer.SetPort(m.stateMachine.msgPort) if type(m.mrssLiveDataFeeds) = "roArray" and m.mrssLiveDataFeeds.Count() > 0 then m.mrssLiveDataFeed = invalid m.currentFeed = invalid m.LaunchWaitForContentTimer() endif endif ' reset indices on entry to the state if type(m.simpleRSSTextTemplateItems) = "roAssociativeArray" then for each simpleRSSId in m.simpleRSSTextTemplateItems simpleRSS = m.simpleRSSTextTemplateItems.Lookup(simpleRSSId) if type(simpleRSS) = "roAssociativeArray" then simpleRSS.currentIndex% = 0 simpleRSS.currentLiveDataFeedIndex% = 0 simpleRSS.rssItemTimer = invalid endif next endif m.liveDataFeeds = CreateObject("roAssociativeArray") for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) m.liveDataFeeds.AddReplace(liveDataFeedName, liveDataFeed) next m.ConfigureBPButtons() m.ConfigureGPIOButtons() m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif if type(m.stateMachine.canvasWidget) <> "roCanvasWidget" then r = CreateObject("roRectangle", m.stateMachine.x%, m.stateMachine.y%, m.stateMachine.width%, m.stateMachine.height%) m.stateMachine.canvasWidget = CreateObject("roCanvasWidget", r) endif m.stateMachine.canvasWidget.EnableAutoRedraw(0) maxLayer% = 1 if type(m.stateMachine.templateObjectsByLayer) = "roArray" then maxLayer% = m.stateMachine.templateObjectsByLayer.Count() endif for i% = 1 to maxLayer% m.stateMachine.canvasWidget.ClearLayer(i%) next ' display background image if it exists file$ = m.backgroundImage$ if file$ <> "" then backgroundImage = {} backgroundImage["filename"] = GetPoolFilePath(m.bsp.assetPoolFiles, file$) backgroundImage["CompositionMode"] = "source" if m.backgroundImageWidth% <= 0 or m.backgroundImageHeight% <= 0 then backgroundImage["targetRect"] = { x: 0, y: 0, w: m.stateMachine.width%, h: m.stateMachine.height% } else m.SetBackgroundImageSizeLocation(backgroundImage) endif m.stateMachine.canvasWidget.SetLayer(backgroundImage, 0) else m.stateMachine.canvasWidget.ClearLayer(0) endif ' build arrays of template items & background colors m.BuildTemplateItems() numLayers% = m.stateMachine.templateObjectsByLayer.Count() for i% = 0 to numLayers% - 1 if type(m.stateMachine.templateObjectsByLayer[i%]) = "roArray" then m.stateMachine.canvasWidget.SetLayer(m.stateMachine.templateObjectsByLayer[i%], i% + 1) endif next m.stateMachine.canvasWidget.EnableAutoRedraw(1) m.stateMachine.ShowCanvasWidget() m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "liveText") ' playback logging m.stateMachine.LogPlayStart("liveText", "") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else if event["EventType"] = "USER_VARIABLES_UPDATED" then if m.TemplateUsesAnyUserVariable() then m.RedisplayTemplateItems() endif return "HANDLED" else if event["EventType"] = "SYSTEM_VARIABLE_UPDATED" then if m.TemplateUsesSystemVariable() then m.RedisplayTemplateItems() endif return "HANDLED" else if event["EventType"] = "USER_VARIABLE_CHANGE" then userVariable = event["UserVariable"] if m.TemplateUsesUserVariable(userVariable) then m.RedisplayTemplateItems() endif return "HANDLED" else if event["EventType"] = "USER_VARIABLES_RESET" then m.RedisplayTemplateItems() return "HANDLED" else if event["EventType"] = "LIVE_DATA_FEED_UPDATE" then liveDataFeed = event["EventData"] m.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) m.RedisplayTemplateItems() return "HANDLED" else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roTimerEvent" then if type(m.waitForContentTimer) = "roTimer" and event.GetSourceIdentity() = m.waitForContentTimer.GetIdentity() then if m.FindMRSSContent() then m.GetMRSSTemplateItem() endif return "HANDLED" endif if type(m.mrssItemTimer) = "roTimer" and event.GetSourceIdentity() = m.mrssItemTimer.GetIdentity() then m.GetMRSSTemplateItem() return "HANDLED" endif if type(m.simpleRSSTextTemplateItems) = "roAssociativeArray" then for each simpleRSSId in m.simpleRSSTextTemplateItems simpleRSS = m.simpleRSSTextTemplateItems.Lookup(simpleRSSId) if type(simpleRSS) = "roAssociativeArray" then if type(simpleRSS.rssItemTimer) = "roTimer" then if event.GetSourceIdentity() = simpleRSS.rssItemTimer.GetIdentity() then itemExists = false liveDataFeed = simpleRSS.rssLiveDataFeeds[simpleRSS.currentLiveDataFeedIndex%] if m.liveDataFeeds.DoesExist(liveDataFeed.name$) then if type(liveDataFeed.articles) = "roArray" then simpleRSS.currentIndex% = simpleRSS.currentIndex% + 1 if simpleRSS.currentIndex% >= liveDataFeed.articles.count() then simpleRSS.currentIndex% = 0 simpleRSS.currentLiveDataFeedIndex% = simpleRSS.currentLiveDataFeedIndex% + 1 if simpleRSS.currentLiveDataFeedIndex% >= simpleRSS.rssLiveDataFeeds.Count() then simpleRSS.currentLiveDataFeedIndex% = 0 endif liveDataFeed = simpleRSS.rssLiveDataFeeds[simpleRSS.currentLiveDataFeedIndex%] if m.liveDataFeeds.DoesExist(liveDataFeed.name$) then if type(liveDataFeed.articles) = "roArray" then itemExists = true endif endif else itemExists = true endif endif endif if itemExists then m.RedisplayTemplateItems() ' restart timer simpleRSS.rssItemTimer.SetElapsed(simpleRSS.displayTime%, 0) simpleRSS.rssItemTimer.Start() endif return "HANDLED" endif endif endif next endif return m.MediaItemEventHandler(event, stateData) else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Function FindMRSSContent() As Boolean ' get the mrss live data feed to start searching with if m.mrssLiveDataFeed = invalid then mrssLiveDataFeedIndex% = 0 else mrssLiveDataFeedIndex% = m.mrssLiveDataFeedIndex% endif startingIndex% = mrssLiveDataFeedIndex% while true mrssLiveDataFeed = m.mrssLiveDataFeeds[mrssLiveDataFeedIndex%] if type(mrssLiveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then currentFeed = mrssLiveDataFeed.feed if currentFeed.ContentExists( mrssLiveDataFeed.assetPoolFiles ) then m.mrssLiveDataFeed = mrssLiveDataFeed m.currentFeed = m.mrssLiveDataFeed.feed m.mrssLiveDataFeedIndex% = mrssLiveDataFeedIndex% m.displayIndex = 0 return true endif endif mrssLiveDataFeedIndex% = mrssLiveDataFeedIndex% + 1 if mrssLiveDataFeedIndex% >= m.mrssLiveDataFeeds.Count() then mrssLiveDataFeedIndex% = 0 endif if mrssLiveDataFeedIndex% = startingIndex% then ' search has wrapped around - nothing was found - continue waiting m.LaunchWaitForContentTimer() return false endif end while End Function Function GetNextMRSSTemplateItem() As Object foundItem = false while not foundItem if m.displayIndex >= m.currentFeed.items.Count() then m.mrssLiveDataFeedIndex% = m.mrssLiveDataFeedIndex% + 1 if m.mrssLiveDataFeedIndex% >= m.mrssLiveDataFeeds.Count() then m.mrssLiveDataFeedIndex% = 0 endif contentFound = m.FindMRSSContent() if not contentFound then return invalid endif endif displayItem = m.currentFeed.items[m.displayIndex] filePath$ = GetPoolFilePath(m.mrssLiveDataFeed.assetPoolFiles, displayItem.url) if filePath$ <> "" then foundItem = true displayItem.filePath$ = filePath$ endif m.displayIndex = m.displayIndex + 1 end while return displayItem End Function Sub GetMRSSTemplateItem() item = m.GetNextMRSSTemplateItem() if type(item) <> "roAssociativeArray" then return endif m.mrssItemTimer.SetElapsed(item.duration, 0) m.mrssItemTimer.Start() if type(m.mrssTitleTemplateItem) = "roAssociativeArray" then m.mrssTitleTemplateItem.textString$ = item.title endif if type(m.mrssDescriptionTemplateItem) = "roAssociativeArray" then m.mrssDescriptionTemplateItem.textString$ = item.description endif if type(m.mrssImageTemplateItem) = "roAssociativeArray" then m.mrssImageTemplateItem.fileName$ = item.filePath$ endif m.RedisplayTemplateItems() End Sub Function STPlayingMediaRSSEventHandler(event As Object, stateData As Object) As Object MEDIA_START = 3 MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' execute entry commands; perform other setup functions m.firstItemDisplayed = false m.PreDrawImage() ' set default transition if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.SetDefaultTransition(m.slideTransition%) endif m.currentFeed = invalid m.pendingFeed = invalid ' see if the designated feed has already been downloaded (doesn't imply content exists) if type(m.liveDataFeed) <> "roAssociativeArray" stop if type(m.liveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then ' create local versions of key objects m.assetCollection = m.liveDataFeed.assetCollection m.assetPoolFiles = m.liveDataFeed.assetPoolFiles m.currentFeed = m.liveDataFeed.feed ' protect the feed that is getting displayed m.ProtectMRSSFeed( "display-" + m.liveDataFeed.name$, m.assetCollection ) m.displayIndex = 0 if not m.currentFeed.ContentExists( m.assetPoolFiles ) then m.LaunchWaitForContentTimer() else m.AdvanceToNextMRSSItem() endif endif m.LaunchTimer() m.bsp.SetTouchRegions(m) m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "mrss") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) return "HANDLED" else if event["EventType"] = "VideoPlaybackFailureEvent" then if m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.AdvanceToNextMRSSItem() return "HANDLED" endif else if event["EventType"] = "MRSS_SPEC_UPDATED" then updatedLiveDataFeed = event["LiveDataFeed"] if updatedLiveDataFeed.name$ = m.liveDataFeed.name$ then m.liveDataFeed = updatedLiveDataFeed ' this seems completely unnecessary if type( m.currentFeed ) <> "roAssociativeArray" or (not m.currentFeed.ContentExists( m.assetPoolFiles ) ) then ' this is the first time that data is available m.pendingFeed = invalid m.currentFeed = m.liveDataFeed.feed m.assetCollection = m.liveDataFeed.assetCollection m.assetPoolFiles = m.liveDataFeed.assetPoolFiles ' protect the feed that is getting displayed m.ProtectMRSSFeed( "display-" + m.liveDataFeed.name$, m.assetCollection ) m.displayIndex = 0 if not m.currentFeed.ContentExists( m.assetPoolFiles ) then m.LaunchWaitForContentTimer() else m.AdvanceToNextMRSSItem() endif else ' feed was updated. play through existing feed until it reaches the end; then switch to new feed. ' note - this does not imply that the feed actually changed. m.pendingFeed = m.liveDataFeed.feed m.pendingAssetCollection = m.liveDataFeed.assetCollection m.pendingAssetPoolFiles = m.liveDataFeed.assetPoolFiles endif endif return "HANDLED" else return m.MediaItemEventHandler(event, stateData) endif endif else if type(event) = "roTimerEvent" then if type(m.imageTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.imageTimeoutTimer.GetIdentity() then if m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.AdvanceToNextMRSSItem() return "HANDLED" endif else if type(m.waitForContentTimer) = "roTimer" and event.GetSourceIdentity() = m.waitForContentTimer.GetIdentity() then if not m.currentFeed.ContentExists( m.assetPoolFiles ) then m.LaunchWaitForContentTimer() else m.displayIndex = 0 m.AdvanceToNextMRSSItem() endif return "HANDLED" endif return m.MediaItemEventHandler(event, stateData) else if type(event) = "roVideoEvent" and type(m.stateMachine.videoPlayer) = "roVideoPlayer" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() and event.GetInt() = MEDIA_END then if m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.AdvanceToNextMRSSItem() return "HANDLED" endif else if m.stateMachine.type$ = "EnhancedAudio" and type(event) = "roAudioEventMx" then if event.GetInt() = MEDIA_START then if not (m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray") then m.AdvanceToNextMRSSItem() return "HANDLED" endif else if event.GetInt() = MEDIA_END then m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.AdvanceToNextMRSSItem() return "HANDLED" endif endif else if IsAudioEvent(m.stateMachine, event) and event.GetInt() = MEDIA_END then if m.AtEndOfFeed() and type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.AdvanceToNextMRSSItem() return "HANDLED" endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub LaunchWaitForContentTimer() if type(m.waitForContentTimer) = "roTimer" then m.waitForContentTimer.Stop() else m.waitForContentTimer = CreateObject("roTimer") m.waitForContentTimer.SetPort(m.bsp.msgPort) endif m.waitForContentTimer.SetElapsed(1, 0) m.waitForContentTimer.Start() End Sub ' need to also consider the case where it's not at the end but there's no more content. Function AtEndOfFeed() As Boolean return m.displayIndex >= m.currentFeed.items.Count() End Function Sub AdvanceToNextMRSSItem() displayedItem = false while not displayedItem if m.displayIndex >= m.currentFeed.items.Count() then m.displayIndex = 0 ' switch to new feed if available if type(m.pendingFeed) = "roAssociativeArray" then m.currentFeed = m.pendingFeed m.assetCollection = m.pendingAssetCollection m.assetPoolFiles = m.pendingAssetPoolFiles m.pendingFeed = invalid ' protect the feed that we're switching to m.ProtectMRSSFeed( "display-" + m.liveDataFeed.name$, m.assetCollection ) if not m.currentFeed.ContentExists( m.assetPoolFiles ) then m.LaunchWaitForContentTimer() return endif endif endif displayItem = m.currentFeed.items[m.displayIndex] filePath$ = GetPoolFilePath(m.assetPoolFiles, displayItem.url) if filePath$ <> "" then m.ProtectMRSSItem( displayItem ) ' with the current code, this may be unnecessary since the entire feed is protected. m.DisplayMRSSItem( displayItem, filePath$ ) ' check return value before doing this?? displayedItem = true endif m.displayIndex = m.displayIndex + 1 end while End Sub Sub ProtectMRSSFeed( name$ As String, assets As Object ) if not m.bsp.feedPool.ProtectAssets( name$, assets ) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif End Sub Sub ProtectMRSSItem( displayItem As Object ) m.playingItemAssetCollection = CreateObject("roAssetCollection") asset = CreateObject("roAssociativeArray") asset.link = displayItem.url asset.name = displayItem.url asset.change_hint = displayItem.guid m.playingItemAssetCollection.AddAsset(asset) ' m.ProtectMRSSFeed( "playing-" + m.currentFeed.title, m.playingItemAssetCollection ) m.ProtectMRSSFeed( "playing-" + m.liveDataFeed.name$, m.playingItemAssetCollection ) End Sub Sub DisplayMRSSItem( displayItem As Object, filePath$ As String ) if isImage(displayItem) then m.imageItem = {} m.imageItem.fileName$ = displayItem.url m.imageItem.filePath$ = filePath$ m.imageItem.userVariable = invalid m.imageItem.slideDelayInterval% = displayItem.duration m.imageItem.useImageBuffer = false ' m.DisplayImage("mrss") m.DrawImage(false) if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif if type(m.imageTimeoutTimer) = "roTimer" then m.imageTimeoutTimer.Stop() endif if type(m.imageTimeoutTimer) <> "roTimer" then m.imageTimeoutTimer = CreateObject("roTimer") m.imageTimeoutTimer.SetPort(m.stateMachine.msgPort) endif m.imageTimeoutTimer.SetElapsed(m.imageItem.slideDelayInterval%, 0) m.imageTimeoutTimer.Start() ' Set transition after image display to be default transition m.stateMachine.imagePlayer.SetDefaultTransition(m.slideTransition%) else if isAudio(displayItem) fileName$ = displayItem.url if type(m.stateMachine.audioPlayer) = "roAudioPlayerMx" then track = {} track["Filename"] = filePath$ track["QueueNext"] = 1 fadeLength% = m.stateMachine.fadelength% * 1000 track["FadeInLength"] = fadeLength% track["FadeOutLength"] = fadeLength% if not m.firstItemDisplayed then track["FadeCurrentPlayNext"] = 0 m.firstItemDisplayed = true endif ok = m.stateMachine.audioPlayer.PlayFile(track) m.stateMachine.LogPlayStart("audioMx", fileName$) else player = invalid if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer else if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then player = m.stateMachine.videoPlayer endif if player <> invalid then player.SetLoopMode(0) aa = { } aa.AddReplace("Filename", filePath$) if type(displayItem.probeData) = "roString" and displayItem.probeData <> "" then aa.AddReplace("ProbeString", displayItem.probeData) endif ok = player.PlayFile(aa) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error playing audio file: " + fileName$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) endif ' playback logging m.stateMachine.LogPlayStart("audio", fileName$) endif endif else m.videoItem = {} m.videoItem.fileName$ = displayItem.url m.videoItem.filePath$ = filePath$ m.videoItem.userVariable = invalid m.videoItem.probeData = invalid m.videoItem.videoDisplayMode% = 0 ' m.LaunchVideo("mrss") m.PlayVideo(false, true) m.stateMachine.ClearImagePlane() ' Set transition to put image up immediately after video is finished if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.SetDefaultTransition(0) endif endif if not m.firstItemDisplayed then m.firstItemDisplayed = true endif End Sub Sub PlayMixerAudio(executeEntryCmds As Boolean, playbackIndex% As Integer, playImmediate As Boolean) loopMode% = 1 if type(m.audioEndEvent) = "roAssociativeArray" then loopMode% = 0 m.stateMachine.audioPlayer.SetLoopMode(loopMode%) ' can't stop since this is code is often just queuing up a track - will this cause any repercussions? ' m.stateMachine.audioPlayer.Stop() ' for now, never stop playback - always use crossfade ' if playImmediate then ' m.stateMachine.audioPlayer.Stop() ' endif if executeEntryCmds then m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) endif file$ = m.audioItem.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) track = CreateObject("roAssociativeArray") track["Filename"] = filePath$ track["QueueNext"] = 1 ' track["Algorithm"] = "muzak" ' track["EncryptionKey"] = playlistPath$ track["UserString"]= playbackIndex% fadeLength% = m.stateMachine.fadelength% * 1000 track["FadeInLength"] = fadeLength% track["FadeOutLength"] = fadeLength% if playImmediate then track["FadeCurrentPlayNext"] = 0 endif ok = m.stateMachine.audioPlayer.PlayFile(track) m.stateMachine.ClearImagePlane() if type(m.audioItem.userVariable) = "roAssociativeArray" then m.audioItem.userVariable.Increment() endif ' playback logging m.stateMachine.LogPlayStart("audioMx", file$) End Sub Sub LaunchMixerAudio(playbackIndex% As Integer, playImmediate As Boolean) m.PrePlayAudio() m.PlayMixerAudio(true, playbackIndex%, playImmediate) m.PostPlayAudio("audioMx") End Sub Function STAudioPlayingEventHandler(event As Object, stateData As Object) As Object MEDIA_END = 8 stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") if m.stateMachine.type$ = "EnhancedAudio" then m.LaunchMixerAudio(-1, true) else m.LaunchAudio("audio") endif return "HANDLED" else if event["EventType"] = "AudioPlaybackFailureEvent" then if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else if m.stateMachine.type$ = "EnhancedAudio" and type(event) = "roAudioEventMx" then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else if IsAudioEvent(m.stateMachine, event) then if event.GetInt() = MEDIA_END then m.bsp.diagnostics.PrintDebug("Audio Event" + stri(event.GetInt())) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaEnd", "", "1") if m.bsp.ProcessMediaEndEvent() then return "HANDLED" endif if type(m.audioEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.audioEndEvent, stateData, "") endif PostMediaEndEvent(m.bsp.msgPort) endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function 'endregion 'region Background Image State Machine ' ************************************************* ' ' Background Image State Machine ' ' ************************************************* Function newBackgroundImageZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = BackgroundImageZoneConstructor zoneHSM.InitialPseudostateHandler = BackgroundImageZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) return zoneHSM End Function Sub BackgroundImageZoneConstructor() m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m ' create players videoPlayer = CreateObject("roVideoPlayer") if type(videoPlayer) <> "roVideoPlayer" then print "videoPlayer creation failed" : stop videoPlayer.SetRectangle(zoneHSM.rectangle) ' Cheetah only if m.bsp.sysInfo.deviceFamily$ = "cheetah" then videoPlayer.ToBack() endif zoneHSM.videoPlayer = videoPlayer zoneHSM.isVideoZone = true m.activeState = m.playlist.firstState if type(m.playlist.firstState) = "roAssociativeArray" then m.previousStateName$ = m.playlist.firstState.id$ else m.previousStateName$ = "" endif m.CreateObjects() End Sub Function BackgroundImageZoneGetInitialState() As Object return m.activeState End Function Function STDisplayingBackgroundImageEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.usbInputBuffer$ = "" m.usbInputLogBuffer$ = "" m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) file$ = m.backgroundImageItem.fileName$ filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) ok = m.stateMachine.videoPlayer.PlayStaticImage(filePath$) if ok = 0 then m.bsp.diagnostics.PrintDebug("Error displaying file in LaunchBackgroundImage: " + file$ + ", " + filePath$) else m.bsp.diagnostics.PrintDebug("LaunchBackgroundImage: display file " + file$) endif if type(m.backgroundImageItem.userVariable) = "roAssociativeArray" then m.backgroundImageItem.userVariable.Increment() endif ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "backgroundImage") return "HANDLED" else if event["EventType"] = "PREPARE_FOR_RESTART" then m.stateMachine.videoPlayer = invalid return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.exitCmds) else return m.MediaItemEventHandler(event, stateData) endif endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function 'endregion 'region Clock State Machine ' ************************************************* ' ' Clock State Machine ' ' ************************************************* Function newClockZoneHSM(bsp As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = ClockZoneConstructor zoneHSM.InitialPseudostateHandler = ClockZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) displayTime$ = zoneXML.zoneSpecificParameters.displayTime.GetText() if lcase(displayTime$) = "true" then zoneHSM.displayTime% = 1 else zoneHSM.displayTime% = 0 endif REM code below doesn't work with BS 2.0 ' rotation$ = zoneXML.zoneSpecificParameters.rotation.GetText() zoneHSM.rotation% = 0 ele = zoneXML.zoneSpecificParameters.GetNamedElements("rotation") if ele.Count() = 1 then rotation$ = ele[0].GetText() if rotation$ = "90" then zoneHSM.rotation% = 3 else if rotation$ = "180" then zoneHSM.rotation% = 2 else if rotation$ = "270" then zoneHSM.rotation% = 1 endif endif widget = zoneXML.zoneSpecificParameters.widget foregroundTextColor = widget.foregroundTextColor zoneHSM.foregroundTextColor% = GetColor(foregroundTextColor.GetAttributes()) backgroundTextColor = widget.backgroundTextColor zoneHSM.backgroundTextColor% = GetColor(backgroundTextColor.GetAttributes()) zoneHSM.font$ = widget.font.GetText() backgroundBitmap = widget.backgroundBitmap if backgroundBitmap.Count() = 1 then backgroundBitmapAttrs = backgroundBitmap.GetAttributes() zoneHSM.backgroundBitmapFile$ = backgroundBitmapAttrs["file"] stretchStr = backgroundBitmapAttrs["stretch"] if stretchStr = "true" then zoneHSM.stretch% = 1 else zoneHSM.stretch% = 0 endif endif safeTextRegion = widget.safeTextRegion if safeTextRegion.Count() = 1 then zoneHSM.safeTextRegionX% = int(val(safeTextRegion.safeTextRegionX.GetText())) zoneHSM.safeTextRegionY% = int(val(safeTextRegion.safeTextRegionY.GetText())) zoneHSM.safeTextRegionWidth% = int(val(safeTextRegion.safeTextRegionWidth.GetText())) zoneHSM.safeTextRegionHeight% = int(val(safeTextRegion.safeTextRegionHeight.GetText())) endif zoneHSM.stClock = zoneHSM.newHState(bsp, "Clock") zoneHSM.stClock.HStateEventHandler = STClockEventHandler zoneHSM.stClock.superState = zoneHSM.stTop return zoneHSM End Function Sub ClockZoneConstructor() m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m globalAA = GetGlobalAA() resourceManager = CreateObject("roResourceManager", globalAA.resourcesFilePath$) if type(resourceManager) = "roResourceManager" then ok = resourceManager.SetLanguage(zoneHSM.language$) if not ok then print "No resources for language ";zoneHSM.language$ : stop a=CreateObject("roAssociativeArray") if zoneHSM.displayTime% = 1 then a["Time"] = 1 a["Date"] = 0 else a["Time"] = 0 a["Date"] = 1 endif a["Rotation"] = zoneHSM.rotation% clockWidget = CreateObject("roClockWidget", zoneHSM.rectangle, resourceManager, a) if type(clockWidget) = "roClockWidget" then zoneHSM.widget = clockWidget if type(zoneHSM.foregroundTextColor%) = "roInt" then zoneHSM.widget.SetForegroundColor(zoneHSM.foregroundTextColor%) endif if type(zoneHSM.backgroundTextColor%) = "roInt" then zoneHSM.widget.SetBackgroundColor(zoneHSM.backgroundTextColor%) endif if zoneHSM.font$ <> "" and zoneHSM.font$ <> "System" then fontPath$ = GetPoolFilePath(m.bsp.assetPoolFiles, zoneHSM.font$) zoneHSM.widget.SetFont(fontPath$) endif if IsString(zoneHSM.backgroundBitmapFile$) then backgroundBitmapFilePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, zoneHSM.backgroundBitmapFile$) zoneHSM.widget.SetBackgroundBitmap(backgroundBitmapFilePath$, zoneHSM.stretch%) endif if type(zoneHSM.safeTextRegionX%) = "roInt" then r = CreateObject("roRectangle", zoneHSM.safeTextRegionX%, zoneHSM.safeTextRegionY%, zoneHSM.safeTextRegionWidth%, zoneHSM.safeTextRegionHeight%) zoneHSM.widget.SetSafeTextRegion(r) r = 0 endif endif endif End Sub Function ClockZoneGetInitialState() As Object return m.stClock End Function Function STClockEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") if type(m.stateMachine.widget) = "roClockWidget" then m.stateMachine.widget.Show() endif return "HANDLED" else if event["EventType"] = "PREPARE_FOR_RESTART" then m.stateMachine.widget = invalid return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif endif stateData.nextState = m.superState return "SUPER" End Function 'endregion 'region Ticker State Machine ' ************************************************* ' ' Ticker State Machine ' ' ************************************************* Function newTickerZoneHSM(bsp As Object, sign As Object, zoneXML As Object) As Object zoneHSM = newHSM() zoneHSM.ConstructorHandler = TickerZoneConstructor zoneHSM.InitialPseudostateHandler = TickerZoneGetInitialState newZoneCommon(bsp, zoneXML, zoneHSM) zoneHSM.rssDownloadPeriodicValue% = sign.rssDownloadPeriodicValue% zoneHSM.rssDownloadTimer = CreateObject("roTimer") zoneHSM.numberOfLines% = int(val(zoneXML.zoneSpecificParameters.textWidget.numberOfLines.GetText())) zoneHSM.delay% = int(val(zoneXML.zoneSpecificParameters.textWidget.delay.GetText())) ' below line doesn't work in BS2.0 ' rotation$ = zoneXML.zoneSpecificParameters.textWidget.rotation.GetText() zoneHSM.rotation% = 0 ele = zoneXML.zoneSpecificParameters.textWidget.GetNamedElements("rotation") if ele.Count() = 1 then rotation$ = ele[0].GetText() if rotation$ = "90" then zoneHSM.rotation% = 3 else if rotation$ = "180" then zoneHSM.rotation% = 2 else if rotation$ = "270" then zoneHSM.rotation% = 1 endif endif alignment$ = zoneXML.zoneSpecificParameters.textWidget.alignment.GetText() if alignment$ = "center" then zoneHSM.alignment% = 1 else if alignment$ = "right" then zoneHSM.alignment% = 2 else zoneHSM.alignment% = 0 endif zoneHSM.scrollingMethod% = int(val(zoneXML.zoneSpecificParameters.textWidget.scrollingMethod.GetText())) widget = zoneXML.zoneSpecificParameters.widget foregroundTextColor = widget.foregroundTextColor zoneHSM.foregroundTextColor% = GetColor(foregroundTextColor.GetAttributes()) backgroundTextColor = widget.backgroundTextColor zoneHSM.backgroundTextColor% = GetColor(backgroundTextColor.GetAttributes()) zoneHSM.font$ = widget.font.GetText() backgroundBitmap = widget.backgroundBitmap if backgroundBitmap.Count() = 1 then backgroundBitmapAttrs = backgroundBitmap.GetAttributes() zoneHSM.backgroundBitmapFile$ = backgroundBitmapAttrs["file"] stretchStr = backgroundBitmapAttrs["stretch"] if stretchStr = "true" then zoneHSM.stretch% = 1 else zoneHSM.stretch% = 0 endif endif safeTextRegion = widget.safeTextRegion if safeTextRegion.Count() = 1 then zoneHSM.safeTextRegionX% = int(val(safeTextRegion.safeTextRegionX.GetText())) zoneHSM.safeTextRegionY% = int(val(safeTextRegion.safeTextRegionY.GetText())) zoneHSM.safeTextRegionWidth% = int(val(safeTextRegion.safeTextRegionWidth.GetText())) zoneHSM.safeTextRegionHeight% = int(val(safeTextRegion.safeTextRegionHeight.GetText())) endif zoneHSM.stRSSDataFeedInitialLoad = zoneHSM.newHState(bsp, "RSSDataFeedInitialLoad") zoneHSM.stRSSDataFeedInitialLoad.HStateEventHandler = STRSSDataFeedInitialLoadEventHandler zoneHSM.stRSSDataFeedInitialLoad.superState = zoneHSM.stTop zoneHSM.stRSSDataFeedPlaying = zoneHSM.newHState(bsp, "RSSDataFeedPlaying") zoneHSM.stRSSDataFeedPlaying.PopulateRSSDataFeedWidget = PopulateRSSDataFeedWidget zoneHSM.stRSSDataFeedPlaying.HStateEventHandler = STRSSDataFeedPlayingEventHandler zoneHSM.stRSSDataFeedPlaying.superState = zoneHSM.stTop return zoneHSM End Function Sub TickerZoneConstructor() m.InitializeZoneCommon(m.bsp.msgPort) zoneHSM = m a=CreateObject("roAssociativeArray") a["PauseTime"] = zoneHSM.delay% a["Rotation"] = zoneHSM.rotation% a["Alignment"] = zoneHSM.alignment% textWidget = CreateObject("roTextWidget", zoneHSM.rectangle, zoneHSM.numberOfLines%, zoneHSM.scrollingMethod%, a) zoneHSM.widget = textWidget if type(zoneHSM.foregroundTextColor%) = "roInt" then zoneHSM.widget.SetForegroundColor(zoneHSM.foregroundTextColor%) endif if type(zoneHSM.backgroundTextColor%) = "roInt" then zoneHSM.widget.SetBackgroundColor(zoneHSM.backgroundTextColor%) endif if zoneHSM.font$ <> "" and zoneHSM.font$ <> "System" then fontPath$ = GetPoolFilePath(m.bsp.assetPoolFiles, zoneHSM.font$) zoneHSM.widget.SetFont(fontPath$) endif if IsString(zoneHSM.backgroundBitmapFile$) then backgroundBitmapFilePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, zoneHSM.backgroundBitmapFile$) zoneHSM.widget.SetBackgroundBitmap(backgroundBitmapFilePath$, zoneHSM.stretch%) endif if type(zoneHSM.safeTextRegionX%) = "roInt" then r = CreateObject("roRectangle", zoneHSM.safeTextRegionX%, zoneHSM.safeTextRegionY%, zoneHSM.safeTextRegionWidth%, zoneHSM.safeTextRegionHeight%) zoneHSM.widget.SetSafeTextRegion(r) r = invalid endif m.includesRSSFeeds = false for each rssDataFeedItem in m.rssDataFeedItems if rssDataFeedItem.isRSSFeed then m.includesRSSFeeds = true endif next End Sub Function TickerZoneGetInitialState() As Object if m.includesRSSFeeds then return m.stRSSDataFeedInitialLoad else return m.stRSSDataFeedPlaying endif End Function Function GetRSSTempFilename() fileName$ = "tmp:/rss" + StripLeadingSpaces(stri(m.rssFileIndex%)) + ".xml" m.rssFileIndex% = m.rssFileIndex% + 1 return fileName$ End Function Function STRSSDataFeedInitialLoadEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") for each rssDataFeedItem in m.stateMachine.rssDataFeedItems rssDataFeedItem.loadAttemptComplete = not rssDataFeedItem.isRSSFeed next return "HANDLED" else if event["EventType"] = "LIVE_DATA_FEED_UPDATE" or event["EventType"] = "LIVE_DATA_FEED_UPDATE_FAILURE" then liveDataFeed = event["EventData"] allLoadsComplete = true for each rssDataFeedItem in m.stateMachine.rssDataFeedItems if rssDataFeedItem.isRSSFeed then if liveDataFeed.name$ = rssDataFeedItem.liveDataFeed.name$ then rssDataFeedItem.loadAttemptComplete = true else if not rssDataFeedItem.loadAttemptComplete then allLoadsComplete = false endif endif next if allLoadsComplete then stateData.nextState = m.stateMachine.STRSSDataFeedPlaying return "TRANSITION" else return "HANDLED" endif else if event["EventType"] = "PREPARE_FOR_RESTART" then m.bsp.diagnostics.PrintDebug(m.id$ + " - PREPARE_FOR_RESTART") m.stateMachine.widget = invalid return "HANDLED" endif endif endif stateData.nextState = m.superState return "SUPER" End Function Sub PopulateRSSDataFeedWidget() ' clear existing strings rssStringCount = m.stateMachine.widget.GetStringCount() m.stateMachine.widget.PopStrings(rssStringCount) ' populate widget with new strings for each rssDataFeedItem in m.stateMachine.rssDataFeedItems if type(rssDataFeedItem.textStrings) = "roArray" then for each textString in rssDataFeedItem.textStrings m.stateMachine.widget.PushString(textString) next else for each article in rssDataFeedItem.liveDataFeed.articles m.stateMachine.widget.PushString(article) next endif next m.stateMachine.widget.Show() End Sub Function STRSSDataFeedPlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.PopulateRSSDataFeedWidget() return "HANDLED" else if event["EventType"] = "LIVE_DATA_FEED_UPDATE" then liveDataFeed = event["EventData"] ' check that the live data feed is for one of the rss feeds rssDataFeedItemLoaded = false for each rssDataFeedItem in m.stateMachine.rssDataFeedItems if rssDataFeedItem.isRSSFeed then if liveDataFeed.name$ = rssDataFeedItem.liveDataFeed.name$ then rssDataFeedItemLoaded = true exit for endif endif next if rssDataFeedItemLoaded then m.PopulateRSSDataFeedWidget() endif else if event["EventType"] = "PREPARE_FOR_RESTART" then m.bsp.diagnostics.PrintDebug(m.id$ + " - PREPARE_FOR_RESTART") m.stateMachine.widget = invalid return "HANDLED" endif endif endif stateData.nextState = m.superState return "SUPER" End Function 'endregion 'region Player State Machine ' ************************************************* ' ' Player State Machine ' ' ************************************************* Function newPlayerStateMachine(bsp As Object) As Object PlayerStateMachine = newHSM() PlayerStateMachine.InitialPseudostateHandler = InitializePlayerStateMachine PlayerStateMachine.bsp = bsp PlayerStateMachine.msgPort = bsp.msgPort PlayerStateMachine.logging = bsp.logging PlayerStateMachine.SetSystemInfo = SetSystemInfo PlayerStateMachine.CheckForUSBUpdate = CheckForUSBUpdate PlayerStateMachine.DisplayUSBUpdateStatus = DisplayUSBUpdateStatus PlayerStateMachine.dataFeedRetryInterval% = 30 PlayerStateMachine.POOL_EVENT_FILE_DOWNLOADED = 1 PlayerStateMachine.POOL_EVENT_FILE_FAILED = -1 PlayerStateMachine.POOL_EVENT_ALL_DOWNLOADED = 2 PlayerStateMachine.POOL_EVENT_ALL_FAILED = -2 PlayerStateMachine.SYNC_ERROR_CANCELLED = -10001 PlayerStateMachine.SYNC_ERROR_CHECKSUM_MISMATCH = -10002 PlayerStateMachine.SYNC_ERROR_EXCEPTION = -10003 PlayerStateMachine.SYNC_ERROR_DISK_ERROR = -10004 PlayerStateMachine.SYNC_ERROR_POOL_UNSATISFIED = -10005 PlayerStateMachine.EVENT_REALIZE_SUCCESS = 101 PlayerStateMachine.stTop = PlayerStateMachine.newHState(bsp, "Top") PlayerStateMachine.stTop.HStateEventHandler = STTopEventHandler PlayerStateMachine.stPlayer = PlayerStateMachine.newHState(bsp, "Player") PlayerStateMachine.stPlayer.HStateEventHandler = STPlayerEventHandler PlayerStateMachine.stPlayer.superState = PlayerStateMachine.stTop PlayerStateMachine.stPlaying = PlayerStateMachine.newHState(bsp, "Playing") PlayerStateMachine.stPlaying.HStateEventHandler = STPlayingEventHandler PlayerStateMachine.stPlaying.superState = PlayerStateMachine.stPlayer PlayerStateMachine.stPlaying.RetrieveLiveDataFeed = RetrieveLiveDataFeed PlayerStateMachine.stPlaying.UpdateTimeClockEvents = UpdateTimeClockEvents PlayerStateMachine.stWaiting = PlayerStateMachine.newHState(bsp, "Waiting") PlayerStateMachine.stWaiting.HStateEventHandler = STWaitingEventHandler PlayerStateMachine.stWaiting.superState = PlayerStateMachine.stPlayer PlayerStateMachine.stUpdatingFromUSB = PlayerStateMachine.newHState(bsp, "UpdatingFromUSB") PlayerStateMachine.stUpdatingFromUSB.HStateEventHandler = STUpdatingFromUSBEventHandler PlayerStateMachine.stUpdatingFromUSB.superState = PlayerStateMachine.stPlayer PlayerStateMachine.stUpdatingFromUSB.BuildFileUpdateList = BuildFileUpdateList PlayerStateMachine.stUpdatingFromUSB.StartUpdateSyncListDownload = StartUpdateSyncListDownload PlayerStateMachine.stUpdatingFromUSB.HandleUSBAssetFetcherEvent = HandleUSBAssetFetcherEvent PlayerStateMachine.stWaitForStorageDetached = PlayerStateMachine.newHState(bsp, "WaitForStorageDetached") PlayerStateMachine.stWaitForStorageDetached.HStateEventHandler = STWaitForStorageDetachedEventHandler PlayerStateMachine.stWaitForStorageDetached.superState = PlayerStateMachine.stTop PlayerStateMachine.topState = PlayerStateMachine.stTop return PlayerStateMachine End Function Function InitializePlayerStateMachine() As Object m.bsp.Restart("") ' determine if a battery charger is present m.powerManager = CreateObject("roPowerManager") s = m.powerManager.GetBatteryStatus() hwVersion = s.Lookup("hardware_version") if hwVersion = invalid then m.bsp.diagnostics.PrintDebug("No charger found") m.chargerPresent = false else m.bsp.diagnostics.PrintDebug("Charger found") m.chargerPresent = true m.powerManager.SetPowerSwitchMode("soft") m.powerManager.SetPort(m.msgPort) ' get initial battery / charger state, save it, and log it m.powerSource$ = m.powerManager.GetPowerSource() batteryStatus = m.powerManager.GetBatteryStatus() m.batteryState$ = "" batteryState = batteryStatus.Lookup("state") if IsString(batteryState) then m.batteryState$ = batteryState endif m.socPercent$ = "" m.socPercentRange% = -1 socPercent = batteryStatus.Lookup("soc_percent") if socPercent <> invalid then socPercentRange% = socPercent / 10 m.socPercent$ = stri(socPercent) if m.socPercentRange% >= 10 then m.socPercentRange% = 9 endif m.socPercentRange% = socPercentRange% endif m.bsp.diagnostics.PrintDebug("InitializePlayerStateMachine: Power Source = " + m.powerSource$ + ", Battery state = " + m.batteryState$ + ", soc percent = " + m.socPercent$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BATTERY_STATUS, m.powerSource$ + chr(9) + m.batteryState$ + chr(9) + m.socPercent$) if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.AddBatteryChargerItem("powerUp", m.powerSource$, m.batteryState$, m.socPercentRange%) endif m.batteryStatusTimer = CreateObject("roTimer") m.batteryStatusTimer.SetPort(m.msgPort) m.batteryStatusTimer.SetElapsed(300, 0) m.batteryStatusTimer.Start() endif ' check for the presence of a USB drive with an update for n% = 1 to 9 usb$ = "USB" + StripLeadingSpaces(stri(n%)) + ":" du = CreateObject("roStorageInfo", usb$) if type(du) = "roStorageInfo" then m.bsp.diagnostics.PrintDebug("### Disc mounted at " + usb$) if m.CheckForUSBUpdate(usb$) then m.storagePath$ = usb$ return m.stUpdatingFromUSB endif endif next activeScheduledPresentation = m.bsp.schedule.activeScheduledEvent if type(activeScheduledPresentation) = "roAssociativeArray" then return m.stPlaying else return m.stWaiting endif End Function Function STPlayerEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") else if event["EventType"] = "PREPARE_FOR_RESTART" then m.bsp.diagnostics.PrintDebug("STPlayerEventHandler - PREPARE_FOR_RESTART") m.bsp.touchScreen = invalid return "HANDLED" else if event["EventType"] = "SWITCH_PRESENTATION" then presentationName$ = event["Presentation"] m.bsp.diagnostics.PrintDebug("STPlayerEventHandler - Switch to presentation " + presentationName$) m.bsp.Restart(presentationName$) stateData.nextState = m.bsp.playerHSM.stPlaying return "TRANSITION" else if event["EventType"] = "CONTENT_UPDATED" then ' new content was downloaded from the network m.bsp.diagnostics.PrintDebug("STPlayerEventHandler - CONTENT_UPDATED") currentSyncSpec = CreateObject("roSyncSpec") if currentSyncSpec.ReadFromFile("current-sync.xml") then m.bsp.assetCollection = currentSyncSpec.GetAssets("download") m.bsp.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.assetPool, m.bsp.assetCollection) endif m.bsp.Restart("") activeScheduledPresentation = m.bsp.schedule.activeScheduledEvent if type(activeScheduledPresentation) = "roAssociativeArray" then stateData.nextState = m.stateMachine.stPlaying else stateData.nextState = m.stateMachine.stWaiting endif return "TRANSITION" endif endif else if type(event) = "roDiskErrorEvent" then aa = event.GetDiskError() diskErrorReport$ = "Time: " + aa["Time"] + " Error: " + aa["source"] + " " + aa["error"] + " " + aa["device"] + " " + aa["param"] m.bsp.diagnostics.PrintDebug("STPlayerEventHandler: Disk error event received: " + diskErrorReport$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_DISK_ERROR, diskErrorReport$) diskErrorMsg = CreateObject("roAssociativeArray") diskErrorMsg["EventType"] = "DISK_ERROR" diskErrorMsg["DiskError"] = aa m.bsp.msgPort.PostMessage(diskErrorMsg) else if type(event) = "roPowerEvent" then powerData = event.GetData() currentState$ = "unknown" requestedState$ = "unknown" currentState = powerData.Lookup("current_state") if currentState <> invalid then currentState$ = currentState endif requestedState = powerData.Lookup("requested_state") if requestedState <> invalid then requestedState$ = requestedState endif if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.AddBatteryChargerItem("power down", "", "", -1) endif m.bsp.diagnostics.PrintDebug("STPlayerEventHandler: Power event received: current state = " + currentState$ + ", requested state = " + requestedState$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_POWER_EVENT, currentState$ + chr(9) + requestedState$) if requestedState$ = "S5" then m.bsp.logging.FlushLogFile() sleep(500) m.stateMachine.powerManager.PowerOff() endif else if type(event) = "roTimerEvent" then if type(m.stateMachine.timer) = "roTimer" and stri(event.GetSourceIdentity()) = stri(m.stateMachine.timer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("STPlayerEventHandler timer event") if not m.bsp.PostponeRestart() then ' send internal message to prepare for restart prepareForRestartEvent = CreateObject("roAssociativeArray") prepareForRestartEvent["EventType"] = "PREPARE_FOR_RESTART" m.bsp.msgPort.PostMessage(prepareForRestartEvent) ' send internal message indicating that new content is available contentUpdatedEvent = CreateObject("roAssociativeArray") contentUpdatedEvent["EventType"] = "CONTENT_UPDATED" m.bsp.msgPort.PostMessage(contentUpdatedEvent) endif return "HANDLED" endif if type(m.stateMachine.batteryStatusTimer) = "roTimer" and stri(event.GetSourceIdentity()) = stri(m.stateMachine.batteryStatusTimer.GetIdentity()) then powerSource$ = m.stateMachine.powerManager.GetPowerSource() batteryStatus = m.stateMachine.powerManager.GetBatteryStatus() batteryState$ = "" batteryState = batteryStatus.Lookup("state") if IsString(batteryState) then batteryState$ = batteryState endif socPercent$ = "" socPercentRange% = -1 socPercent = batteryStatus.Lookup("soc_percent") if socPercent <> invalid then socPercentRange% = socPercent / 10 socPercent$ = stri(socPercent) if socPercentRange% >= 10 then socPercentRange% = 9 endif endif if powerSource$ <> m.stateMachine.powerSource$ or batteryState$ <> m.stateMachine.batteryState$ or socPercentRange% < m.stateMachine.socPercentRange% then m.stateMachine.powerSource$ = powerSource$ m.stateMachine.batteryState$ = batteryState$ m.stateMachine.socPercent$ = socPercent$ m.stateMachine.socPercentRange% = socPercentRange% m.bsp.diagnostics.PrintDebug("STPlayerEventHandler - charger data changed: Power Source = " + powerSource$ + ", Battery state = " + batteryState$ + ", soc percent = " + socPercent$) if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.AddBatteryChargerItem("powerManagerStatus", powerSource$, batteryState$, socPercentRange%) endif endif m.bsp.diagnostics.PrintDebug("STPlayerEventHandler: Power Source = " + powerSource$ + ", Battery state = " + batteryState$ + ", soc percent = " + socPercent$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BATTERY_STATUS, powerSource$ + chr(9) + batteryState$ + chr(9) + socPercent$) m.stateMachine.batteryStatusTimer.SetElapsed(15, 0) m.stateMachine.batteryStatusTimer.Start() return "HANDLED" endif if type(m.bsp.logging.cutoverTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.bsp.logging.cutoverTimer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("STPlayerEventHandler cutover logs timer event") m.bsp.logging.HandleTimerEvent() m.bsp.LogActivePresentation() return "HANDLED" endif endif else if type(event) = "roHdmiEdidChanged" then edid = m.bsp.videoMode.GetEdidIdentity(true) UpdateEdidValues(edid, m.bsp.sysInfo) edid = invalid m.bsp.UpdateEdidUserVariables(true) systemVariableChanged = CreateObject("roAssociativeArray") systemVariableChanged["EventType"] = "SYSTEM_VARIABLE_UPDATED" m.bsp.msgPort.PostMessage(systemVariableChanged) ' else if type(event) = "roAssetFetcherProgressEvent" then ' print "### File download progress ";event.GetFileName();" unknown" else if type(event) = "roAssetFetcherEvent" then userData$ = event.GetUserData() for each liveDataFeedName in m.bsp.liveDataFeeds if userData$ = liveDataFeedName then liveDataFeed = m.bsp.liveDataFeeds.Lookup(userData$) liveDataFeed.HandleLiveDataFeedContentDownloadAssetFetcherEvent(event) return "HANDLED" endif next else if type(event) = "roNetworkAttached" or type(event) = "roNetworkDetached" then networkInterface% = event.GetInt() if type(event) = "roNetworkAttached" then nc = CreateObject("roNetworkConfiguration", networkInterface%) if type(nc) = "roNetworkConfiguration" then currentConfig = nc.GetCurrentConfig() if type(currentConfig) = "roAssociativeArray" then if currentConfig.ip4_address <> "" then if networkInterface% = 0 then m.bsp.sysInfo.ipAddressWired$ = currentConfig.ip4_address else if networkInterface% = 1 then m.bsp.sysInfo.ipAddressWireless$ = currentConfig.ip4_address endif endif endif endif nc = invalid else if networkInterface% = 0 then m.bsp.sysInfo.ipAddressWired$ = "Invalid" else if networkInterface% = 1 then m.bsp.sysInfo.ipAddressWireless$ = "Invalid" endif endif m.bsp.UpdateIPAddressUserVariables(true) systemVariableChanged = CreateObject("roAssociativeArray") systemVariableChanged["EventType"] = "SYSTEM_VARIABLE_UPDATED" m.bsp.msgPort.PostMessage(systemVariableChanged) else if type(event) = "roControlEvent" then eventIdentity = stri(event.GetSourceIdentity()) blcIndex% = -1 if type(m.bsp.blcDiagnostics[0]) = "roControlPort" and stri(m.bsp.blcDiagnostics[0].GetIdentity()) = eventIdentity then blcIndex% = 0 else if type(m.bsp.blcDiagnostics[1]) = "roControlPort" and stri(m.bsp.blcDiagnostics[1].GetIdentity()) = eventIdentity then blcIndex% = 1 else if type(m.bsp.blcDiagnostics[2]) = "roControlPort" and stri(m.bsp.blcDiagnostics[2].GetIdentity()) = eventIdentity then blcIndex% = 2 endif if blcIndex% <> -1 then blcIdentifier$ = "BLC" + stri(blcIndex%) + ":" endif ' event types coming back from the blc400 REPORT_UNDER_EVENT% = &h20 REPORT_OVER_EVENT% = &h21 REPORT_MISSING% = &h22 REPORT_NORMAL% = &h23 ' event ADC channels MAIN_ADC% = 0 LED_ADC_COMP1% = 1 LED_ADC_COMP2% = 2 LED_ADC_COMP3% = 3 LED_ADC_COMP4% = 4 LED_ADC_OCOMP1% = 5 LED_ADC_OCOMP2% = 6 LED_ADC_OCOMP3% = 7 LED_ADC_OCOMP4% = 8 ch% = event.GetEventByte(1) adc% = event.GetEventWord(2) if (event.GetEventByte(0) = REPORT_UNDER_EVENT%) then event$ = "REPORT_UNDER_EVENT: " else if (event.GetEventByte(0) = REPORT_OVER_EVENT%) then event$ = "REPORT_OVER_EVENT: " else if (event.GetEventByte(0) = REPORT_MISSING%) then event$ = "REPORT_MISSING: " else if (event.GetEventByte(0) = REPORT_NORMAL%) then event$ = "REPORT_NORMAL: " else event$ = "" endif nextChannel% = -1 if event$ = "" then msg$ = blcIdentifier$ + "Unexpected control event(0): " + Stri(event.GetEventByte(0)) else if (ch% = MAIN_ADC%) then msg$ = blcIdentifier$ + event$ + "Main Power: " + Stri(adc%) nextChannel% = &h01 else if (ch% = LED_ADC_COMP1%) or (ch% = LED_ADC_OCOMP1%) then msg$ = blcIdentifier$ + event$ + "Channel A: " + Stri(adc%) nextChannel% = &h02 else if (ch% = LED_ADC_COMP2%) or (ch% = LED_ADC_OCOMP2%) then msg$ = blcIdentifier$ + event$ + "Channel B: " + Stri(adc%) nextChannel% = &h04 else if (ch% = LED_ADC_COMP3%) or (ch% = LED_ADC_OCOMP3%) then msg$ = blcIdentifier$ + event$ + "Channel C: " + Stri(adc%) nextChannel% = &h08 else if (ch% = LED_ADC_COMP4%) or (ch% = LED_ADC_OCOMP4%) then msg$ = blcIdentifier$ + event$ + "Channel D: " + Stri(adc%) else msg$ = blcIdentifier$ + "Unknown Power Error: " + Stri(ch%) + ": " + Stri(adc%) endif m.bsp.diagnostics.PrintDebug(msg$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BLC400_STATUS, msg$) if nextChannel% <> -1 and blcIndex% <> -1 then m.bsp.CheckBLCStatus(m.bsp.blcs[blcIndex%], nextChannel%) endif else if type(event) = "roStorageAttached" then storagePath$ = event.GetString() ' check for existence of upgrade file if m.stateMachine.CheckForUSBUpdate(storagePath$) then m.stateMachine.storagePath$ = storagePath$ stateData.nextState = m.stateMachine.stUpdatingFromUSB return "TRANSITION" else actionsXMLFilePath$ = event.GetString() + "actions.xml" actionsSpec$ = ReadAsciiFile(actionsXMLFilePath$) if actionsSpec$ <> "" then actionsXML = CreateObject("roXMLElement") actionsXML.Parse(actionsSpec$) if type(actionsXML.action) = "roXMLList" then if actionsXML.action.Count() > 0 then attributes = actionsXML.GetAttributes() displayStatus$ = attributes.Lookup("displayStatus") if lcase(displayStatus$) = "true" then videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode = invalid r = CreateObject("roRectangle", 0, 0, resX, resY) twParams = CreateObject("roAssociativeArray") twParams.LineCount = 1 twParams.TextMode = 2 twParams.Rotation = 0 twParams.Alignment = 1 tw=CreateObject("roTextWidget",r,1,2,twParams) tw.PushString("") tw.Show() r=CreateObject("roRectangle",0,resY/2-resY/64,resX,resY/32) tw=CreateObject("roTextWidget",r,1,2,twParams) displayStatus = true else displayStatus = false endif deletedLogFiles = false errorEncountered = false for each action in actionsXML.action action$ = action.GetText() if action$ = "copyLogs" then if displayStatus then tw.Clear() tw.PushString("Copying log files.") tw.Show() endif ok = m.bsp.logging.CopyAllLogFiles(storagePath$) if ok then m.bsp.diagnostics.PrintDebug("CopyAllLogFiles completed successfully") else errorEncountered = true m.bsp.diagnostics.PrintDebug("CopyAllLogFiles failed") if displayStatus then tw.Clear() tw.PushString("Error encountered while copying log files.") tw.Show() sleep(5000) endif exit for endif else if action$ = "deleteLogs" then if displayStatus then tw.Clear() tw.PushString("Deleting log files.") tw.Show() endif m.bsp.logging.DeleteAllLogFiles() m.bsp.diagnostics.PrintDebug("DeleteAllLogFiles complete") deletedLogFiles = true else if action$ = "resetVariables" then if displayStatus then tw.Clear() tw.PushString("Resetting variables.") tw.Show() endif m.bsp.ResetVariables() m.bsp.diagnostics.PrintDebug("Resetting variables complete") else if action$ = "copyVariablesDB" then if m.bsp.variablesDBExists then if displayStatus then tw.Clear() tw.PushString("Copying variables database.") tw.Show() endif serialNumber$ = m.bsp.sysInfo.deviceUniqueID$ dtLocal = m.bsp.systemTime.GetLocalDateTime() year$ = Right(stri(dtLocal.GetYear()), 2) month$ = StripLeadingSpaces(stri(dtLocal.GetMonth())) if len(month$) = 1 then month$ = "0" + month$ endif day$ = StripLeadingSpaces(stri(dtLocal.GetDay())) if len(day$) = 1 then day$ = "0" + day$ endif hour$ = StripLeadingSpaces(stri(dtLocal.GetHour())) if len(hour$) = 1 then hour$ = "0" + day$ endif minute$ = StripLeadingSpaces(stri(dtLocal.GetMinute())) if len(minute$) = 1 then minute$ = "0" + minute$ endif ' date$ = year$ + month$ + day$ + hour$ + minute$ date$ = year$ + month$ + day$ fileName$ = "BrightSignVariables." + serialNumber$ + "-" + date$ + ".txt" filePath$ = storagePath$ + fileName$ variablesFile = CreateObject("roCreateFile", filePath$) if type(variablesFile) = "roCreateFile" then m.bsp.ExportVariablesDBToAsciiFile(variablesFile) ' determine if write was successful ' partial fix - only works if card was full before this step variablesFile.SeekToEnd() position% = variablesFile.CurrentPosition() if position% = 0 then errorEncountered = true m.bsp.diagnostics.PrintDebug("copyVariablesDB failed - fileLength = 0") else m.bsp.diagnostics.PrintDebug("Wrote variables file to " + filePath$) endif variablesFile = invalid else errorEncountered = true m.bsp.diagnostics.PrintDebug("copyVariablesDB failed - create file failed") endif if errorEncountered then if displayStatus then tw.Clear() tw.PushString("Error encountered while copying variables database.") tw.Show() sleep(5000) endif exit for endif else if displayStatus then tw.Clear() tw.PushString("No variables to copy.") tw.Show() sleep(3000) endif else if action$ = "reboot" then if displayStatus then tw.Clear() tw.PushString("Finalizing data writes, do not remove your drive yet.") tw.Show() EjectStorage(storagePath$) tw.Clear() tw.PushString("Data capture complete - you may remove your drive. The system will reboot shortly.") tw.Show() sleep(5000) tw.Clear() else EjectStorage(storagePath$) endif RebootSystem() return "HANDLED" endif next if displayStatus then tw.Clear() tw.PushString("Finalizing data writes, do not remove your drive yet.") tw.Show() EjectStorage(storagePath$) tw.Clear() if errorEncountered then tw.PushString("Data capture failed - you may remove your drive.") else tw.PushString("Data capture completed successfully - you may remove your drive.") endif tw.Show() sleep(5000) tw = invalid endif ' if the log files were deleted but the system is not rebooting, open a log file if m.bsp.logging.loggingEnabled then m.bsp.logging.OpenOrCreateCurrentLog() endif endif endif endif endif stateData.nextState = m.superState return "SUPER" End Function Sub EjectStorage(storagePath$ As String) ok = EjectDrive(storagePath$) if not ok then sleep(30000) endif End Sub Function STWaitingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' set a timer for when the system should become active again if type(m.bsp.schedule.nextScheduledEventTime) = "roDateTime" then dateTime = m.bsp.schedule.nextScheduledEventTime newTimer = CreateObject("roTimer") newTimer.SetTime(dateTime.GetHour(), dateTime.GetMinute(), 0) newTimer.SetDate(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay()) newTimer.SetDayOfWeek(dateTime.GetDayOfWeek()) newTimer.SetPort(m.stateMachine.msgPort) newTimer.Start() m.stateMachine.timer = newTimer endif return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif endif stateData.nextState = m.superState return "SUPER" End Function Function STPlayingEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' set a timer for when the current presentation should end activeScheduledPresentation = m.bsp.schedule.activeScheduledEvent if type(activeScheduledPresentation) = "roAssociativeArray" then if m.bsp.schedule.activeScheduledEventEndDateTime <> invalid then endDateTime = m.bsp.schedule.activeScheduledEventEndDateTime newTimer = CreateObject("roTimer") newTimer.SetTime(endDateTime.GetHour(), endDateTime.GetMinute(), 0) newTimer.SetDate(endDateTime.GetYear(), endDateTime.GetMonth(), endDateTime.GetDay()) newTimer.SetDayOfWeek(endDateTime.GetDayOfWeek()) newTimer.SetPort(m.stateMachine.msgPort) newTimer.Start() m.stateMachine.timer = newTimer m.bsp.diagnostics.PrintDebug("Set STPlayingEventHandler timer to " + endDateTime.GetString()) endif ' check for live data feeds that include content (either MRSS or content for Media Lists / PlayFiles). for each of them, check to see if the feed and/or content already exists. for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) liveDataFeed.ReadFeedContent() next ' load live data feeds m.liveDataFeeds = CreateObject("roAssociativeArray") m.bsp.liveDataFeedsByTimer = CreateObject("roAssociativeArray") for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) m.RetrieveLiveDataFeed(liveDataFeed) next ' launch playback m.bsp.StartPlayback() endif return "HANDLED" else if event["EventType"] = "UPDATE_DATA_FEED" then dataFeedName$ = event["Name"] for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) if dataFeedName$ = liveDataFeedName then liveDataFeed.forceUpdate = true m.RetrieveLiveDataFeed(liveDataFeed) endif next else if event["EventType"] = "UPDATE_DATA_FEED_BY_CATEGORY" then categoryName$ = event["Name"] for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) if liveDataFeed.title$ = categoryName$ then liveDataFeed.forceUpdate = true m.RetrieveLiveDataFeed(liveDataFeed) endif next else if event["EventType"] = "UPDATE_ALL_DATA_FEEDS" then for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) if liveDataFeed.autoGenerateUserVariables then liveDataFeed.forceUpdate = true m.RetrieveLiveDataFeed(liveDataFeed) endif next else if event["EventType"] = "CONTENT_DATA_FEED_LOADED" then sign = m.bsp.sign for each zone in sign.zonesHSM for each stateName in zone.stateTable state = zone.stateTable[stateName] if state.type$ = "playFile" then if type(state.liveDataFeed) = "roAssociativeArray" and event["Name"] = state.liveDataFeed.name$ then if type(state.liveDataFeed.assetPoolFiles) = "roAssetPoolFiles" then state.PopulatePlayFileFromLiveDataFeed() return "HANDLED" endif endif endif next next else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif else if type(event) = "roTimerEvent" then eventIdentity$ = stri(event.GetSourceIdentity()) if m.bsp.liveDataFeedsByTimer.DoesExist(eventIdentity$) then liveDataFeed = m.bsp.liveDataFeedsByTimer.Lookup(eventIdentity$) m.RetrieveLiveDataFeed(liveDataFeed) endif else if type(event) = "roUrlEvent" then eventIdentity$ = stri(event.GetSourceIdentity()) if m.liveDataFeeds.DoesExist(eventIdentity$) then liveDataFeed = m.liveDataFeeds.Lookup(eventIdentity$) m.liveDataFeeds.Delete(eventIdentity$) if event.GetResponseCode() = 200 or event.GetResponseCode() = 0 then liveDataFeedDownloadFailed = false liveDataFeed.articles = CreateObject("roArray", 1, true) liveDataFeed.articleTitles = CreateObject("roArray", 1, true) liveDataFeed.articlesByTitle = CreateObject("roAssociativeArray") userVariables = m.bsp.currentUserVariables parser$ = liveDataFeed.parser$ if parser$ <> "" then ERR_NORMAL_END = &hFC retVal = Eval(liveDataFeed.parser$ + "(liveDataFeed.rssFileName$, liveDataFeed.articles, liveDataFeed.articlesByTitle, userVariables)") if retVal <> ERR_NORMAL_END then ' log the failure m.bsp.diagnostics.PrintDebug("Failure invoking Eval to parse live text data feed: return value = " + stri(retVal) + ", parser is " + liveDataFeed.parser$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_LIVE_TEXT_PLUGIN_FAILURE, stri(retVal) + chr(9) + liveDataFeed.parser$) endif else if type(liveDataFeed.isJSON) = "roBoolean" and liveDataFeed.isJSON then jsonString=ReadAsciiFile(liveDataFeed.rssFileName$) json = ParseJSON(jsonString) numItemsAdded% = 0 for each jsonItem in json text$ = jsonItem.text liveDataFeed.articles.Push(text$) liveDataFeed.articleTitles.Push(text$) liveDataFeed.articlesByTitle.AddReplace(text$, text$) numItemsAdded% = numItemsAdded% + 1 if liveDataFeed.restrictNumberOfItems and (numItemsAdded% >= liveDataFeed.numberOfItemsToDisplay%) then exit for endif next else parser = CreateObject("roRssParser") parser.ParseFile(liveDataFeed.rssFileName$) article = parser.GetNextArticle() while type(article) = "roRssArticle" title = article.GetTitle() description = article.GetDescription() liveDataFeed.articles.Push(description) liveDataFeed.articleTitles.Push(title) liveDataFeed.articlesByTitle.AddReplace(title, description) article = parser.GetNextArticle() endwhile endif endif ' parsing for autogenerated user variables - make it conditional on using autogenerated user variables ' check for uv parser. if exists, send rss feed, array of elements where each element is title, description, mediaURL. also need to get the title back (not sure how). ' if no parser exists, parse it here, filling in the array as above. determine title of feed. liveDataFeed.items = CreateObject("roArray", 1, true) if liveDataFeed.autoGenerateUserVariables then if liveDataFeed.uvParser$ <> "" then ERR_NORMAL_END = &hFC retVal = Eval(liveDataFeed.uvParser$ + "(liveDataFeed.rssFileName$, liveDataFeed.items, userVariables, m.bsp)") if retVal <> ERR_NORMAL_END then ' log the failure m.bsp.diagnostics.PrintDebug("Failure invoking Eval to parse live data feed for user variables: return value = " + stri(retVal) + ", parser is " + liveDataFeed.uvParser$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_LIVE_TEXT_PLUGIN_FAILURE, stri(retVal) + chr(9) + liveDataFeed.uvParser$) endif else dataFeedXML = CreateObject("roXMLElement") dataFeedXML.Parse(ReadAsciiFile(liveDataFeed.rssFileName$)) liveDataFeed.title$ = dataFeedXML.channel.title.gettext() allItemsXML = dataFeedXML.channel.item position% = 0 for each itemXML in allItemsXML item = {} item.title$ = itemXML.title.gettext() item.description$ = itemXML.description.gettext() mediaContent=itemXML.GetNamedElements("media:content")[0] if mediaContent = invalid then item.mediaUrl$ = "" else item.mediaUrl$= mediaContent.GetAttributes()["url"] endif item.position% = position% position% = position% + 1 liveDataFeed.items.push(item) end for endif endif liveDataFeed.isMRSSFeed = liveDataFeed.FeedIsMRSS( liveDataFeed.rssFileName$ ) if liveDataFeed.downloadContent then liveDataFeed.DownloadLiveFeedContent() else if liveDataFeed.isMRSSFeed then liveDataFeed.DownloadMRSSContent() endif if liveDataFeed.autoGenerateUserVariables then m.bsp.CreateUserVariablesFromDataFeed(liveDataFeed) endif DeleteFile(liveDataFeed.rssFileName$) ' update user variables if type(userVariables) = "roAssociativeArray" then updatedUserVariables = { } for each title in liveDataFeed.articlesByTitle ' update user variable if appropriate if userVariables.DoesExist(title) then userVariable = userVariables.Lookup(title) if type(userVariable.liveDataFeed) = "roAssociativeArray" and userVariable.liveDataFeed.name$ = liveDataFeed.name$ then description = liveDataFeed.articlesByTitle[title] userVariable.SetCurrentValue(description, true) updatedUserVariables.AddReplace(title, userVariable) endif endif next m.UpdateTimeClockEvents(updatedUserVariables) endif ' send internal message indicating that the data feed has been updated liveTextDataUpdatedEvent = CreateObject("roAssociativeArray") liveTextDataUpdatedEvent["EventType"] = "LIVE_DATA_FEED_UPDATE" liveTextDataUpdatedEvent["EventData"] = liveDataFeed m.bsp.msgPort.PostMessage(liveTextDataUpdatedEvent) liveDataFeed.forceUpdate = false updateInterval% = liveDataFeed.updateInterval% else liveDataFeedDownloadFailed = true url$ = liveDataFeed.url.GetCurrentParameterValue() m.bsp.diagnostics.PrintDebug("Failure downloading Live Text Data feed " + url$ + ", responseCode = " + stri(event.GetResponseCode())) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_LIVE_TEXT_FEED_DOWNLOAD_FAILURE, url$ + chr(9) + stri(event.GetResponseCode()) + chr(9) + event.GetFailureReason()) ' send internal message indicating that the data feed download failed liveTextDataUpdatedEvent = CreateObject("roAssociativeArray") liveTextDataUpdatedEvent["EventType"] = "LIVE_DATA_FEED_UPDATE_FAILURE" liveTextDataUpdatedEvent["EventData"] = liveDataFeed m.bsp.msgPort.PostMessage(liveTextDataUpdatedEvent) updateInterval% = m.stateMachine.dataFeedRetryInterval% endif ' set a timer to update live data feed liveDataFeed.RestartLiveDataFeedDownloadTimer(updateInterval%) endif endif stateData.nextState = m.superState return "SUPER" End Function Sub UpdateDataFeed(parameters As Object) updateDataFeedParameter = parameters["dataFeed"] dataFeedName$ = updateDataFeedParameter.GetCurrentParameterValue() updateDataFeedMsg = CreateObject("roAssociativeArray") updateDataFeedMsg["EventType"] = "UPDATE_DATA_FEED" updateDataFeedMsg["Name"] = dataFeedName$ m.msgPort.PostMessage(updateDataFeedMsg) End Sub Sub CreateUserVariablesFromDataFeed(liveDataFeed As Object) ' get the section name if lcase(liveDataFeed.userVariableAccess$) = "shared" then sectionName$ = "Shared" else sectionName$ = m.activePresentation$ endif sectionId% = m.GetDBSectionId(sectionName$) if sectionId% < 0 then m.AddDBSection(sectionName$) sectionId% = m.GetDBSectionId(sectionName$) endif categoryName$ = liveDataFeed.title$ categoryId% = m.GetDBCategoryId(sectionId%, categoryName$) ' desired behavour ' if the category does not exist, create it and populate it ' if the category does exist and the update interval is not Once ' add new user variables ' for matching user variables, update the current value but leave the default alone ' remove deleted user variables if categoryId% < 0 then m.AddDBCategory(sectionId%, categoryName$) categoryId% = m.GetDBCategoryId(sectionId%, categoryName$) for each item in liveDataFeed.items m.AddDBVariable(categoryId%, item.title$, item.description$, item.mediaUrl$, item.position%) next else if liveDataFeed.updateInterval% > 0 or liveDataFeed.forceUpdate then ' get existing user variables for this section (and Shared) / category userVariablesList = m.GetUserVariablesByCategoryList(categoryName$) ' populate an associative array with the items in the live data feed dataFeedItems = {} for each item in liveDataFeed.items dataFeedItems.AddReplace(item.title$, item) next ' delete each user variable that exists but is not in the data feed; update variables that remain userVariables = {} for each userVariable in userVariablesList userVariableName$ = userVariable.name$ userVariables.AddReplace(userVariableName$, userVariable) if not dataFeedItems.DoesExist(userVariableName$) then ' delete variable m.DeleteDBVariable(categoryId%, userVariableName$) else ' update variable dataFeedItem = dataFeedItems.Lookup(userVariableName$) m.UpdateDBVariable(categoryId%, dataFeedItem.title$, dataFeedItem.description$) m.UpdateDBVariableMediaUrl(categoryId%, dataFeedItem.title$, dataFeedItem.mediaUrl$) m.UpdateDBVariablePosition(categoryId%, dataFeedItem.title$, dataFeedItem.position%) endif next ' add new variables for each item in liveDataFeed.items if not userVariables.DoesExist(item.title$) then m.AddDBVariable(categoryId%, item.title$, item.description$, item.mediaUrl$, item.position%) endif next endif End Sub Function STUpdatingFromUSBEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' stop all playback, clear screen and background if type(m.bsp.sign) = "roAssociativeArray" and type(m.bsp.sign.zonesHSM) = "roArray" then for each zoneHSM in m.bsp.sign.zonesHSM if IsAudioPlayer(zoneHSM.audioPlayer) then zoneHSM.audioPlayer.Stop() zoneHSM.audioPlayer = invalid endif if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Stop() zoneHSM.videoPlayer = invalid endif zoneHSM.ClearImagePlane() next endif m.bsp.sign = invalid videoMode = CreateObject("roVideoMode") resX = videoMode.GetResX() resY = videoMode.GetResY() videoMode.SetBackgroundColor(0) videoMode = invalid ' display update message on the screen twParams = CreateObject("roAssociativeArray") twParams.LineCount = 1 twParams.TextMode = 2 twParams.Rotation = 0 twParams.Alignment = 1 r=CreateObject("roRectangle",0,resY/2-resY/64,resX,resY/32) m.stateMachine.usbUpdateTW = CreateObject("roTextWidget",r,1,2,twParams) ' m.stateMachine.DisplayUSBUpdateStatus("Content update in progress. Do not remove the drive.") m.stateMachine.DisplayUSBUpdateStatus("Update in progress. Do not remove the drive.") ' read the sync specs and proceed with update if appropriate performingFWUpdate = false syncSpecFile$ = "/update/local-sync.xml" syncSpecFilePath$ = m.stateMachine.storagePath$ + syncSpecFile$ if not FileExists(syncSpecFilePath$) then performingFWUpdate = true syncSpecFile$ = "/fwUpdate/fw-sync.xml" syncSpecFilePath$ = m.stateMachine.storagePath$ + syncSpecFile$ endif m.stateMachine.newSync = CreateObject("roSyncSpec") ok = m.stateMachine.newSync.ReadFromFile(syncSpecFilePath$) if not ok then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_READ_SYNCSPEC_FAILURE, "newSync") m.bsp.diagnostics.PrintDebug("### USB drive has an invalid sync spec.") usbUpdateErrorEvent = CreateObject("roAssociativeArray") usbUpdateErrorEvent["EventType"] = "USB_UPDATE_ERROR" usbUpdateErrorEvent["Message"] = "Update files are corrupt." m.stateMachine.msgPort.PostMessage(usbUpdateErrorEvent) return "HANDLED" endif ' perform security check usbContentUpdatePassword$ = m.bsp.registrySettings.usbContentUpdatePassword$ ' check for signature file signaturePath$ = m.stateMachine.storagePath$ + "/update/signature.txt" signatureFile = CreateObject("roReadFile", signaturePath$) if type(signatureFile) = "roReadFile" then signatureFileExists = true signature$ = ReadAsciiFile(signaturePath$) else signatureFileExists = false endif signatureFile = invalid securityError = false if not signatureFileExists then ' no signature file and passphrase => error; no signature file and no passphrase => proceed with update if usbContentUpdatePassword$ <> "" then securityError = true endif else if usbContentUpdatePassword$ <> "" then ok = m.stateMachine.newSync.VerifySignature(signature$, usbContentUpdatePassword$) if not ok then securityError = true endif endif if securityError then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_USB_UPDATE_SECURITY_ERROR, "local-sync") m.bsp.diagnostics.PrintDebug("### USB update security error.") usbUpdateErrorEvent = CreateObject("roAssociativeArray") usbUpdateErrorEvent["EventType"] = "USB_UPDATE_ERROR" usbUpdateErrorEvent["Message"] = "Update failed - an incorrect password was provided." m.stateMachine.msgPort.PostMessage(usbUpdateErrorEvent) return "HANDLED" endif if performingFWUpdate then globalAA = GetGlobalAA() globalAA.bsp.msgPort.DeferWatchdog(120) assetCollection = m.stateMachine.newSync.GetAssets("download") path$ = m.stateMachine.storagePath$ + "/fwUpdate/pool" pool = CreateObject("roAssetPool", path$) realizer = CreateObject("roAssetRealizer", pool, "/") event = realizer.Realize(assetCollection) ' check return value if event.GetEvent() <> m.stateMachine.EVENT_REALIZE_SUCCESS then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_REALIZE_FAILURE, stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Realize failed " + stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason() ) m.stateMachine.waitForStorageDetachedMsg$ = "Update failure (Realize). Remove the drive and the system will reboot." usbTransitionEvent = CreateObject("roAssociativeArray") usbTransitionEvent["EventType"] = "USB_PERFORM_TRANSITION" m.stateMachine.msgPort.PostMessage(usbTransitionEvent) return "HANDLED" endif ' write out a new sync spec file?? m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### USB FIRWMARE UPDATE FILE DOWNLOAD COMPLETE") m.stateMachine.waitForStorageDetachedMsg$ = "Firmware update complete. Remove the drive and the system will reboot." usbTransitionEvent = CreateObject("roAssociativeArray") usbTransitionEvent["EventType"] = "USB_PERFORM_TRANSITION" m.stateMachine.msgPort.PostMessage(usbTransitionEvent) return "HANDLED" else m.stateMachine.currentSync = CreateObject("roSyncSpec") if not m.stateMachine.currentSync.ReadFromFile("local-sync.xml") then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_READ_SYNCSPEC_FAILURE, "local-sync") m.bsp.diagnostics.PrintDebug("### Unable to read local-sync.xml.") usbUpdateErrorEvent = CreateObject("roAssociativeArray") usbUpdateErrorEvent["EventType"] = "USB_UPDATE_ERROR" usbUpdateErrorEvent["Message"] = "Unable to perform update." m.stateMachine.msgPort.PostMessage(usbUpdateErrorEvent) return "HANDLED" endif if m.stateMachine.newSync.EqualTo(m.stateMachine.currentSync) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "NO") m.bsp.diagnostics.PrintDebug("### USB drive has a spec that matches current-sync. Nothing more to do.") m.stateMachine.newSync = invalid updateSyncSpecMatchesEvent = CreateObject("roAssociativeArray") updateSyncSpecMatchesEvent["EventType"] = "UPDATE_SYNC_SPEC_MATCHES" m.stateMachine.msgPort.PostMessage(updateSyncSpecMatchesEvent) return "HANDLED" endif m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "YES") m.BuildFileUpdateList(m.stateMachine.newSync) errorMsg = m.StartUpdateSyncListDownload() if type(errorMsg) = "roString" then usbUpdateErrorEvent = CreateObject("roAssociativeArray") usbUpdateErrorEvent["EventType"] = "USB_UPDATE_ERROR" usbUpdateErrorEvent["Message"] = errorMsg m.stateMachine.msgPort.PostMessage(usbUpdateErrorEvent) endif endif return "HANDLED" else if event["EventType"] = "USB_PERFORM_TRANSITION" then stateData.nextState = m.stateMachine.stWaitForStorageDetached return "TRANSITION" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") else if event["EventType"] = "UPDATE_SYNC_SPEC_MATCHES" then m.stateMachine.waitForStorageDetachedMsg$ = "The content on the USB drive matches the content on the card. Remove the drive and the system will reboot." stateData.nextState = m.stateMachine.stWaitForStorageDetached return "TRANSITION" else if event["EventType"] = "USB_UPDATE_ERROR" then errorMsg$ = event["Message"] m.stateMachine.waitForStorageDetachedMsg$ = errorMsg$ + " Remove the drive and the system will reboot." stateData.nextState = m.stateMachine.stWaitForStorageDetached return "TRANSITION" else if event["EventType"] = "PREPARE_FOR_RESTART" or event["EventType"] = "SWITCH_PRESENTATION" or event["EventType"] = "CONTENT_UPDATED" then ' consume these events during USB updates return "HANDLED" endif endif else if type(event) = "roTimerEvent" or type(event) = "roUrlEvent" then ' consume these events during USB updates return "HANDLED" else if type(event) = "roAssetFetcherProgressEvent" then m.bsp.diagnostics.PrintDebug("### File update progress " + event.GetFileName() + str(event.GetCurrentFilePercentage())) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_PROGRESS, event.GetFileName() + chr(9) + str(event.GetCurrentFilePercentage())) fileIndex% = event.GetFileIndex() fileItem = m.stateMachine.newSync.GetFile("download", fileIndex%) if event.GetCurrentFilePercentage() = 0 then m.stateMachine.DisplayUSBUpdateStatus("Downloading " + event.GetFileName() + " (" + StripLeadingSpaces(stri(fileIndex%)) + " of " + StripLeadingSpaces(stri(m.listOfUpdateFiles.Count())) + "). Do not remove the drive.") endif return "HANDLED" else if (type(event) = "roAssetFetcherEvent") then if event.GetUserData() = "USB" then nextState = m.HandleUSBAssetFetcherEvent(event) if type(nextState) = "roAssociativeArray" then stateData.nextState = nextState return "TRANSITION" endif return "HANDLED" endif ' this event is currently not received - the script gets the typical roAssetPoolEvent errors. ' else if type(event) = "roStorageDetached" then ' m.stateMachine.DisplayUSBUpdateStatus("The drive was removed before the update was complete - the system will reboot shortly.") ' sleep(5000) ' RebootSystem() endif stateData.nextState = m.superState return "SUPER" End Function Sub BuildFileUpdateList(syncSpec As Object) fileInPoolStatus = m.bsp.assetPool.QueryFiles(syncSpec) m.listOfUpdateFiles = CreateObject("roArray", 10, true) for each fileName in fileInPoolStatus fileInPool = fileInPoolStatus.Lookup(fileName) if not fileInPool then m.listOfUpdateFiles.push(fileName) endif next End Sub Function StartUpdateSyncListDownload() As Object m.bsp.diagnostics.PrintDebug("### Start usb update sync list download") m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_DOWNLOAD_START, "") m.bsp.assetPool.ReserveMegabytes(50) m.assetFetcher = CreateObject("roAssetFetcher", m.bsp.assetPool) m.assetFetcher.SetUserData("USB") m.assetFetcher.SetPort(m.stateMachine.msgPort) if not m.bsp.assetPool.ProtectAssets("USB", m.stateMachine.currentSync) then ' don't allow download to delete current files m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.assetPool.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.assetPool.GetFailureReason()) return "Update failure (ProtectFiles)." endif prefix$ = "file:///" + m.stateMachine.storagePath$ + "/update/" m.assetFetcher.SetRelativeLinkPrefix(prefix$) if not m.assetFetcher.AsyncDownload(m.stateMachine.newSync) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_IMMEDIATE_FAILURE, m.assetFetcher.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### AsyncDownload failed: " + m.assetFetcher.GetFailureReason()) return "Update failure (AsyncDownload)." endif return invalid End Function Function HandleUSBAssetFetcherEvent(event As Object) As Object m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### usb update pool_event") if (event.GetEvent() = m.stateMachine.POOL_EVENT_FILE_DOWNLOADED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_COMPLETE, event.GetName()) m.bsp.diagnostics.PrintDebug("### File downloaded " + event.GetName()) else if (event.GetEvent() = m.stateMachine.POOL_EVENT_FILE_FAILED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_FAILURE, event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### File failed " + event.GetName() + ": " + event.GetFailureReason()) else if (event.GetEvent() = m.stateMachine.POOL_EVENT_ALL_FAILED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_FAILURE, event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Sync failed: " + event.GetFailureReason()) m.stateMachine.waitForStorageDetachedMsg$ = "Update failure (file failure). Remove the drive and the system will reboot." return m.stateMachine.stWaitForStorageDetached else if (event.GetEvent() = m.stateMachine.POOL_EVENT_ALL_DOWNLOADED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_DOWNLOAD_COMPLETE, "") m.bsp.diagnostics.PrintDebug("### All files downloaded") oldSyncSpecScriptsOnly = m.stateMachine.currentSync.FilterFiles("download", { group: "script" } ) newSyncSpecScriptsOnly = m.stateMachine.newSync.FilterFiles("download", { group: "script" } ) rebootRequired = false if not oldSyncSpecScriptsOnly.FilesEqualTo(newSyncSpecScriptsOnly) then ' Protect all the media files that the current sync spec is using in case we fail part way through and need to continue using it. if not (m.bsp.assetPool.ProtectAssets("current", m.stateMachine.currentSync) and m.bsp.assetPool.ProtectAssets("new", m.stateMachine.newSync)) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.assetPool.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.assetPool.GetFailureReason()) m.stateMachine.waitForStorageDetachedMsg$ = "Update failure (ProtectFiles). Remove the drive and the system will reboot." return m.stateMachine.stWaitForStorageDetached endif realizer = CreateObject("roAssetRealizer", m.bsp.assetPool, "/") globalAA = GetGlobalAA() globalAA.bsp.msgPort.DeferWatchdog(120) event = realizer.Realize(newSyncSpecScriptsOnly) realizer = invalid if event.GetEvent() <> m.stateMachine.EVENT_REALIZE_SUCCESS then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_REALIZE_FAILURE, stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Realize failed " + stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason() ) m.stateMachine.waitForStorageDetachedMsg$ = "Update failure (Realize). Remove the drive and the system will reboot." return m.stateMachine.stWaitForStorageDetached endif endif ' Save to current-sync.xml then do cleanup if not m.stateMachine.newSync.WriteToFile("local-sync.xml") then stop m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### USB UPDATE FILE DOWNLOAD COMPLETE") m.stateMachine.waitForStorageDetachedMsg$ = "Content update complete. Remove the drive and the system will reboot." return m.stateMachine.stWaitForStorageDetached endif return invalid End Function Function STWaitForStorageDetachedEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") ' check to see if the drive is still in the device du = CreateObject("roStorageInfo", m.stateMachine.storagePath$) if type(du) = "roStorageInfo" then m.stateMachine.DisplayUSBUpdateStatus(m.stateMachine.waitForStorageDetachedMsg$) else m.stateMachine.DisplayUSBUpdateStatus("The drive was removed before the update was complete - the system will reboot shortly.") sleep(5000) RebootSystem() endif return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") else if event["EventType"] = "PREPARE_FOR_RESTART" or event["EventType"] = "SWITCH_PRESENTATION" or event["EventType"] = "CONTENT_UPDATED" then ' consume these events during USB updates return "HANDLED" endif endif else if type(event) = "roTimerEvent" or type(event) = "roUrlEvent" then ' consume these events during USB updates return "HANDLED" else if type(event) = "roStorageDetached" then m.stateMachine.logging.FlushLogFile() RebootSystem() endif stateData.nextState = m.superState return "SUPER" End Function Function CheckForUSBUpdate(storagePath$ As String) As Object syncSpecFilePath$ = storagePath$ + "/update/local-sync.xml" if FileExists(syncSpecFilePath$) return true syncSpecFilePath$ = storagePath$ + "/fwUpdate/fw-sync.xml" if FileExists(syncSpecFilePath$) return true return false End Function Sub DisplayUSBUpdateStatus(status$ As String) m.usbUpdateTW.Clear() m.usbUpdateTW.PushString(status$) m.usbUpdateTW.Show() End Sub Sub UpdateTimeClockEvents(updatedUserVariables As Object) ' m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_LIVE_TEXT_FEED_DOWNLOAD_FAILURE, url$ + chr(9) + stri(event.GetResponseCode()) + chr(9) + event.GetFailureReason()) if type(m.bsp.sign) = "roAssociativeArray" then sign = m.bsp.sign if type(sign.zonesHSM) = "roArray" then for each zoneHSM in sign.zonesHSM if type(zoneHSM.activeState) = "roAssociativeArray" then activeState = zoneHSM.activeState if type(activeState.timeClockEvents) = "roArray" then for each timeClockEvent in activeState.timeClockEvents if type(timeClockEvent.userVariable) = "roAssociativeArray" then updatedUserVariable = updatedUserVariables.Lookup(timeClockEvent.userVariableName$) if type(updatedUserVariable) = "roAssociativeArray" then dateTime$ = updatedUserVariable.GetCurrentValue() dateTime = FixDateTime(dateTime$) if type(dateTime) = "roDateTime" then ' if timer is in the future, set it. if IsTimeoutInFuture(dateTime) setTimer = true m.bsp.diagnostics.PrintDebug("Set timeout to " + dateTime.GetString()) else setTimer = false endif if type(timeClockEvent.timer) = "roTimer" then timeClockEvent.timer.Stop() else if setTimer then timeClockEvent.timer = CreateObject("roTimer") endif if setTimer then timeClockEvent.timer.SetDateTime(dateTime) timeClockEvent.timer.SetPort(zoneHSM.msgPort) timeClockEvent.timer.Start() endif else m.bsp.diagnostics.PrintDebug("Timeout specification " + dateTime$ + " is invalid") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_INVALID_DATE_TIME_SPEC, dateTime$) endif endif endif next endif endif next endif endif End Sub Function GenerateNonce(systemTime As Object) As String ' Nonce just needs to be a reasonably unique alphanumeric string bytes = CreateObject("roByteArray") bytes.FromAsciiString(systemTime.GetUtcDateTime().GetString()) nonce = bytes.ToBase64String() ' Remove non word chars - just replace with arbitrary character rx = CreateObject("roRegEx","\W","") return rx.ReplaceAll(nonce,"z") End Function Function GenerateTimestamp(systemTime As Object) As String return systemTime.GetUtcDateTime().ToSecondsSinceEpoch().ToStr() End Function Function GenerateOAuthSignature(urlTransfer As Object, authenticationData As Object, nonce As String, timestamp As String) As String url$ = urlTransfer.GetUrl() ' Generate sorted array of all parameters (header and query string) paramArray = CreateObject("roArray", 8, TRUE) ' First, get parameters from URL query string queryIndex = instr(1, url$, "?") if queryIndex > 0 then params = mid(url$, queryIndex+1).tokenize("&") for each param in params nameval = param.tokenize("=") if nameval.Count() > 1 then paramItem = CreateObject("roAssociativeArray") paramItem.name = nameVal[0] paramItem.value = nameVal[1] paramArray.push(paramItem) endif next endif ' Next, add the oauth parameters paramArray.push( { name: "oauth_consumer_key", value: authenticationData.ConsumerKey } ) paramArray.push( { name: "oauth_nonce", value: urlTransfer.Escape(nonce) } ) paramArray.push( { name: "oauth_signature_method", value: "HMAC-SHA1" } ) paramArray.push( { name: "oauth_timestamp", value: timestamp } ) paramArray.push( { name: "oauth_token", value: urlTransfer.Escape(authenticationData.AuthToken) } ) paramArray.push( { name: "oauth_version", value: "1.0" } ) ' Now sort the parameter array max = paramArray.Count() sortedParamArray = CreateObject("roArray", max, FALSE) while (paramArray.Count() > 0) index = 0 for i = 1 to paramArray.Count()-1 if paramArray[i].name < paramArray[index].name then index = i endif end for sortedParamArray.push(paramArray[index]) paramArray.Delete(index) end while ' normalized parameter string normParams$ = "" for i = 0 to sortedParamArray.Count()-1 normParams$ = normParams$ + urlTransfer.Escape(sortedParamArray[i].name) + "=" + urlTransfer.Escape(sortedParamArray[i].value) if i < sortedParamArray.Count()-1 then normParams$ = normParams$ + "&" endif end for ' create signature base string if authenticationData.DoesExist("HttpMethod") and type(authenticationData.HttpMethod) = "roString" then sigBase$ = authenticationData.HttpMethod + "&" else sigBase$ = "GET&" endif if (queryIndex > 0) normUrl$ = left(url$,queryIndex-1) else normUrl$ = url$ endif sigBase$ = sigBase$ + urlTransfer.Escape(normUrl$) + "&" + urlTransfer.Escape(normParams$) 'print "OAuth base string: " + sigBase$ hashGen = CreateObject("roHashGenerator", "SHA1") hashGen.SetObfuscatedHmacKey(authenticationData.EncryptedTwitterSecrets) ' get hash - we will NOT escape this here - that will be done when we generate the header hashStr$ = hashGen.hash(sigBase$).ToBase64String() return hashStr$ End Function Function GetOAuthAuthorizationHeader(urlTransfer As Object, authenticationData As Object) As String systemTime = CreateObject("roSystemTime") nonce = GenerateNonce(systemTime) timestamp = GenerateTimestamp(systemTime) s = "OAuth " s = s + "oauth_consumer_key=" + chr(34) + urlTransfer.Escape(authenticationData.ConsumerKey) + chr(34) + "," s = s + "oauth_nonce=" + chr(34) + nonce + chr(34) + "," s = s + "oauth_signature=" + chr(34) + urlTransfer.Escape(GenerateOAuthSignature(urlTransfer, authenticationData, nonce, timestamp)) + chr(34) + "," s = s + "oauth_signature_method=" + chr(34) + "HMAC-SHA1" + chr(34) + "," s = s + "oauth_timestamp=" + chr(34) + timestamp + chr(34) + "," s = s + "oauth_token=" + chr(34) + urlTransfer.Escape(authenticationData.AuthToken) + chr(34) + "," s = s + "oauth_version=" + chr(34) + "1.0" + chr(34) 'print "Auth header: " + s return s End Function Sub RetrieveLiveDataFeed(liveDataFeed As Object) url$ = liveDataFeed.url.GetCurrentParameterValue() auth = liveDataFeed.authenticationData m.bsp.diagnostics.PrintDebug("### Retrieve live text data feed from " + url$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_RETRIEVE_LIVE_TEXT_FEED, url$) liveDataFeed.rssURLXfer = CreateObject("roUrlTransfer") liveDataFeed.rssURLXfer.SetUrl(url$) liveDataFeed.rssURLXfer.SetPort(m.bsp.msgPort) liveDataFeed.rssFileName$ = m.bsp.GetRSSTempFilename() liveDataFeed.rssURLXfer.SetTimeout(55000) ' 55 second timeout ' Set authorization header, if authentication data is present if type(auth) = "roAssociativeArray" and type(auth.AuthType) = "roString" then if auth.AuthType = "OAuth 1.0a" then ' Set OAuth header if not liveDataFeed.rssURLXfer.AddHeader("Authorization", GetOAuthAuthorizationHeader(liveDataFeed.rssURLXfer, auth)) then m.bsp.diagnostics.PrintDebug("Failed to set authorization header, reason: " + liveDataFeed.rssURLXfer.GetFailureReason()) endif endif endif binding% = GetBinding(m.bsp.textFeedsXfersEnabledWired, m.bsp.textFeedsXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for RetrieveLiveDataFeed is " + stri(binding%)) ok = liveDataFeed.rssURLXfer.BindToInterface(binding%) if not ok then stop liveDataFeed.rssURLXfer.AsyncGetToFile(liveDataFeed.rssFileName$) m.liveDataFeeds.AddReplace(stri(liveDataFeed.rssURLXfer.GetIdentity()), liveDataFeed) End Sub Function GetRSSDownloadInterval(rssDownloadSpec As Object) As Integer rssDownloadPeriodicValue% = 86400 if type(rssDownloadSpec) = "roXMLList" then if rssDownloadSpec.Count() > 0 then rssDownloadSpecAttrs = rssDownloadSpec.GetAttributes() rssDownloadSpecType = rssDownloadSpecAttrs["type"] if rssDownloadSpecType = "periodic" then rssDownloadPeriodicValue% = val(rssDownloadSpecAttrs["value"]) endif endif endif return rssDownloadPeriodicValue% End Function 'endregion 'region Networking State Machine ' ************************************************* ' ' Networking State Machine ' ' ************************************************* Function newNetworkingStateMachine(bsp As Object, msgPort As Object) As Object NetworkingStateMachine = newHSM() NetworkingStateMachine.InitialPseudostateHandler = InitializeNetworkingStateMachine NetworkingStateMachine.bsp = bsp NetworkingStateMachine.msgPort = msgPort NetworkingStateMachine.systemTime = bsp.systemTime NetworkingStateMachine.diagnostics = bsp.diagnostics NetworkingStateMachine.logging = bsp.logging NetworkingStateMachine.RestartContentDownloadWindowStartTimer = RestartContentDownloadWindowStartTimer NetworkingStateMachine.RestartContentDownloadWindowEndTimer = RestartContentDownloadWindowEndTimer NetworkingStateMachine.RestartHeartbeatsWindowStartTimer = RestartHeartbeatsWindowStartTimer NetworkingStateMachine.RestartHeartbeatsWindowEndTimer = RestartHeartbeatsWindowEndTimer NetworkingStateMachine.RestartWindowStartTimer = RestartWindowStartTimer NetworkingStateMachine.RestartWindowEndTimer = RestartWindowEndTimer NetworkingStateMachine.SetSystemInfo = SetSystemInfo NetworkingStateMachine.AddMiscellaneousHeaders = AddMiscellaneousHeaders NetworkingStateMachine.AddLocalToBSNHeaders = AddLocalToBSNHeaders NetworkingStateMachine.DeviceDownloadItems = CreateObject("roArray", 8, true) NetworkingStateMachine.DeviceDownloadItemsPendingUpload = CreateObject("roArray", 8, true) NetworkingStateMachine.AddDeviceDownloadItem = AddDeviceDownloadItem NetworkingStateMachine.UploadDeviceDownload = UploadDeviceDownload NetworkingStateMachine.FileListPendingUpload = true NetworkingStateMachine.DeviceDownloadProgressItems = CreateObject("roAssociativeArray") NetworkingStateMachine.DeviceDownloadProgressItemsPendingUpload = CreateObject("roAssociativeArray") NetworkingStateMachine.PushDeviceDownloadProgressItem = PushDeviceDownloadProgressItem NetworkingStateMachine.AddDeviceDownloadProgressItem = AddDeviceDownloadProgressItem NetworkingStateMachine.UploadDeviceDownloadProgressItems = UploadDeviceDownloadProgressItems NetworkingStateMachine.UploadDeviceDownloadProgressFileList = UploadDeviceDownloadProgressFileList NetworkingStateMachine.BuildFileDownloadList = BuildFileDownloadList NetworkingStateMachine.SendTrafficUpload = SendTrafficUpload NetworkingStateMachine.UploadTrafficDownload = UploadTrafficDownload NetworkingStateMachine.UploadMRSSTrafficDownload = UploadMRSSTrafficDownload NetworkingStateMachine.pendingMRSSContentDownloaded# = 0 NetworkingStateMachine.lastMRSSContentDownloaded# = 0 NetworkingStateMachine.EventItems = CreateObject("roArray", 8, true) NetworkingStateMachine.AddEventItem = AddEventItem NetworkingStateMachine.UploadEvent = UploadEvent NetworkingStateMachine.DeviceErrorItems = CreateObject("roArray", 8, true) NetworkingStateMachine.AddDeviceErrorItem = AddDeviceErrorItem NetworkingStateMachine.UploadDeviceError = UploadDeviceError NetworkingStateMachine.BatteryChargerItems = CreateObject("roArray", 8, true) NetworkingStateMachine.AddBatteryChargerItem = AddBatteryChargerItem NetworkingStateMachine.UploadBatteryCharger = UploadBatteryCharger NetworkingStateMachine.deviceDownloadProgressUploadURL = invalid NetworkingStateMachine.deviceDownloadUploadURL = invalid NetworkingStateMachine.trafficDownloadUploadURL = invalid NetworkingStateMachine.mrssTrafficDownloadUploadURL = invalid NetworkingStateMachine.eventUploadURL = invalid NetworkingStateMachine.deviceErrorUploadURL = invalid NetworkingStateMachine.batteryChargerUploadURL = invalid NetworkingStateMachine.LogProtectFilesFailure = LogProtectFilesFailure ' logging NetworkingStateMachine.UploadLogFiles = UploadLogFiles NetworkingStateMachine.UploadLogFileHandler = UploadLogFileHandler NetworkingStateMachine.uploadLogFileURLXfer = invalid NetworkingStateMachine.uploadLogFileURL$ = "" NetworkingStateMachine.uploadLogFolder = "logs" NetworkingStateMachine.uploadLogArchiveFolder = "archivedLogs" NetworkingStateMachine.uploadLogFailedFolder = "failedLogs" NetworkingStateMachine.enableLogDeletion = true NetworkingStateMachine.SendHeartbeat = SendHeartbeat NetworkingStateMachine.AddUploadHeaders = AddUploadHeaders NetworkingStateMachine.RebootAfterEventsSent = RebootAfterEventsSent NetworkingStateMachine.WaitForTransfersToComplete = WaitForTransfersToComplete NetworkingStateMachine.ResetDownloadTimerToDoRetry = ResetDownloadTimerToDoRetry NetworkingStateMachine.retryInterval% = 60 NetworkingStateMachine.numRetries% = 0 NetworkingStateMachine.maxRetries% = 3 NetworkingStateMachine.ResetHeartbeatTimerToDoRetry = ResetHeartbeatTimerToDoRetry NetworkingStateMachine.heartbeatRetryInterval% = 60 NetworkingStateMachine.numHeartbeatRetries% = 0 NetworkingStateMachine.maxHeartbeatRetries% = 3 NetworkingStateMachine.fileDownloadFailureCount% = 0 NetworkingStateMachine.maxFileDownloadFailures% = 3 NetworkingStateMachine.POOL_EVENT_FILE_DOWNLOADED = 1 NetworkingStateMachine.POOL_EVENT_FILE_FAILED = -1 NetworkingStateMachine.POOL_EVENT_ALL_DOWNLOADED = 2 NetworkingStateMachine.POOL_EVENT_ALL_FAILED = -2 NetworkingStateMachine.SYNC_ERROR_CANCELLED = -10001 NetworkingStateMachine.SYNC_ERROR_CHECKSUM_MISMATCH = -10002 NetworkingStateMachine.SYNC_ERROR_EXCEPTION = -10003 NetworkingStateMachine.SYNC_ERROR_DISK_ERROR = -10004 NetworkingStateMachine.SYNC_ERROR_POOL_UNSATISFIED = -10005 NetworkingStateMachine.EVENT_REALIZE_SUCCESS = 101 NetworkingStateMachine.stTop = NetworkingStateMachine.newHState(bsp, "Top") NetworkingStateMachine.stTop.HStateEventHandler = STTopEventHandler NetworkingStateMachine.stNetworkScheduler = NetworkingStateMachine.newHState(bsp, "NetworkScheduler") NetworkingStateMachine.stNetworkScheduler.HStateEventHandler = STNetworkSchedulerEventHandler NetworkingStateMachine.stNetworkScheduler.superState = NetworkingStateMachine.stTop NetworkingStateMachine.stWaitForTimeout = NetworkingStateMachine.newHState(bsp, "WaitForTimeout") NetworkingStateMachine.stWaitForTimeout.HStateEventHandler = STWaitForTimeoutEventHandler NetworkingStateMachine.stWaitForTimeout.superState = NetworkingStateMachine.stNetworkScheduler NetworkingStateMachine.stRetrievingSyncList = NetworkingStateMachine.newHState(bsp, "RetrievingSyncList") NetworkingStateMachine.stRetrievingSyncList.StartSync = StartSync NetworkingStateMachine.stRetrievingSyncList.SyncSpecXferEvent = SyncSpecXferEvent NetworkingStateMachine.stRetrievingSyncList.HStateEventHandler = STRetrievingSyncListEventHandler NetworkingStateMachine.stRetrievingSyncList.superState = NetworkingStateMachine.stNetworkScheduler NetworkingStateMachine.stRetrievingSyncList.ConfigureNetwork = ConfigureNetwork NetworkingStateMachine.stRetrievingSyncList.UpdateRegistrySetting = UpdateRegistrySetting NetworkingStateMachine.stDownloadingSyncFiles = NetworkingStateMachine.newHState(bsp, "DownloadingSyncFiles") NetworkingStateMachine.stDownloadingSyncFiles.StartSyncListDownload = StartSyncListDownload NetworkingStateMachine.stDownloadingSyncFiles.HandleAssetFetcherEvent = HandleAssetFetcherEvent NetworkingStateMachine.stDownloadingSyncFiles.HStateEventHandler = STDownloadingSyncFilesEventHandler NetworkingStateMachine.stDownloadingSyncFiles.superState = NetworkingStateMachine.stNetworkScheduler NetworkingStateMachine.topState = NetworkingStateMachine.stTop return NetworkingStateMachine End Function Function InitializeNetworkingStateMachine() As Object ' determine whether or not to enable proxy mode support m.proxy_mode = false ' if caching is enabled, set parameter indicating whether downloads are only allowed from the cache m.downloadOnlyIfCached = false ' combination of proxies and wireless not yet supported nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then if nc.GetProxy() <> "" then m.proxy_mode = true OnlyDownloadIfCached$ = m.bsp.registrySettings.OnlyDownloadIfCached$ if OnlyDownloadIfCached$ = "true" then m.downloadOnlyIfCached = true endif endif nc = invalid ' Load up the current sync specification so we have it ready m.currentSync = CreateObject("roSyncSpec") if type(m.currentSync) <> "roSyncSpec" then return false if not m.currentSync.ReadFromFile("current-sync.xml") and not m.currentSync.ReadFromFile("localToBSN-sync.xml") then m.diagnostics.PrintDebug("### No current sync state available") return false endif m.accountName$ = m.currentSync.LookupMetadata("server", "account") base$ = m.currentSync.LookupMetadata("client", "base") nextURL = GetURL(base$, m.currentSync.LookupMetadata("client", "next")) m.eventURL = GetURL(base$, m.currentSync.LookupMetadata("client", "event")) m.deviceDownloadProgressURL = GetURL(base$, m.currentSync.LookupMetadata("client", "devicedownloadprogress")) m.deviceDownloadURL = GetURL(base$, m.currentSync.LookupMetadata("client", "devicedownload")) m.trafficDownloadURL = GetURL(base$, m.currentSync.LookupMetadata("client", "trafficdownload")) m.deviceErrorURL = GetURL(base$, m.currentSync.LookupMetadata("client", "deviceerror")) m.uploadLogFileURL$ = GetURL(base$, m.currentSync.LookupMetadata("client", "uploadlogs")) m.batteryChargerURL$ = GetURL(base$, m.currentSync.LookupMetadata("client", "batteryCharger")) m.heartbeatURL$ = GetURL(base$, m.currentSync.LookupMetadata("client", "heartbeat")) timezone = m.currentSync.LookupMetadata("client", "timezone") if timezone <> "" then m.systemTime.SetTimeZone(timezone) endif m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("### Current active sync list suggests next URL of " + nextURL) if nextURL = "" then stop if m.eventURL = "" then stop m.user$ = m.currentSync.LookupMetadata("server", "user") m.password$ = m.currentSync.LookupMetadata("server", "password") if m.user$ <> "" or m.password$ <> "" then m.setUserAndPassword = true enableUnsafeAuthentication$ = m.currentSync.LookupMetadata("server", "enableUnsafeAuthentication") if lcase(enableUnsafeAuthentication$) = "true" then m.enableUnsafeAuthentication = true else m.enableUnsafeAuthentication = false endif else m.setUserAndPassword = false m.enableUnsafeAuthentication = false endif useWireless$ = m.currentSync.LookupMetadata("client", "useWireless") if not m.modelSupportsWifi then useWireless$ = "no" if useWireless$ = "yes" then m.useWireless = true else m.useWireless = false endif ' get net connect parameters, setup timer, and rate limits timeBetweenNetConnects$ = m.currentSync.LookupMetadata("client", "timeBetweenNetConnects") contentDownloadsRestricted = m.currentSync.LookupMetadata("client", "contentDownloadsRestricted") contentDownloadRangeStart = m.currentSync.LookupMetadata("client", "contentDownloadRangeStart") contentDownloadRangeLength = m.currentSync.LookupMetadata("client", "contentDownloadRangeLength") timeBetweenHeartbeats$ = m.currentSync.LookupMetadata("client", "timeBetweenHeartbeats") heartbeatsRestricted = m.currentSync.LookupMetadata("client", "heartbeatsRestricted") heartbeatsRangeStart = m.currentSync.LookupMetadata("client", "heartbeatsRangeStart") heartbeatsRangeLength = m.currentSync.LookupMetadata("client", "heartbeatsRangeLength") m.wiredRateLimits = {} if m.useWireless then rateLimitModeOutsideWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitModeOutsideWindow_2") rateLimitRateOutsideWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitRateOutsideWindow_2") rateLimitModeInWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitModeInWindow_2") rateLimitRateInWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitRateInWindow_2") else rateLimitModeOutsideWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitModeOutsideWindow") rateLimitRateOutsideWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitRateOutsideWindow") rateLimitModeInWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitModeInWindow") rateLimitRateInWindowWired$ = m.currentSync.LookupMetadata("client", "rateLimitRateInWindow") endif SetRateLimitValues(true, m.wiredRateLimits, rateLimitModeOutsideWindowWired$, rateLimitRateOutsideWindowWired$, rateLimitModeInWindowWired$, rateLimitRateInWindowWired$) m.wirelessRateLimits = {} if m.useWireless then rateLimitModeOutsideWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitModeOutsideWindow") rateLimitRateOutsideWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitRateOutsideWindow") rateLimitModeInWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitModeInWindow") rateLimitRateInWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitRateInWindow") else rateLimitModeOutsideWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitModeOutsideWindow_2") rateLimitRateOutsideWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitRateOutsideWindow_2") rateLimitModeInWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitModeInWindow_2") rateLimitRateInWindowWireless$ = m.currentSync.LookupMetadata("client", "rateLimitRateInWindow_2") endif SetRateLimitValues(true, m.wirelessRateLimits, rateLimitModeOutsideWindowWireless$, rateLimitRateOutsideWindowWireless$, rateLimitModeInWindowWireless$, rateLimitRateInWindowWireless$) ' if the values above are not found, try to retrieve them from the registry. for simple networking (pre BA 2.3), only the ' initial sync spec will include these values if timeBetweenNetConnects$ = "" then timeBetweenNetConnects$ = m.bsp.registrySettings.timeBetweenNetConnects$ if timeBetweenNetConnects$ = "" then print "Error: timeBetweenNetConnects not found in either the sync spec or the registry":stop contentDownloadsRestricted = m.bsp.registrySettings.contentDownloadsRestricted if contentDownloadsRestricted = "" then print "Error: contentDownloadsRestricted not set in registry":stop contentDownloadRangeStart = m.bsp.registrySettings.contentDownloadRangeStart contentDownloadRangeLength = m.bsp.registrySettings.contentDownloadRangeLength endif ' check for timeBetweenNetConnects override tbnco$ = m.bsp.registrySettings.tbnco$ if tbnco$ <> "" then timeBetweenNetConnects$ = tbnco$ endif m.timeBetweenNetConnects% = val(timeBetweenNetConnects$) m.diagnostics.PrintDebug("### Time between net connects = " + timeBetweenNetConnects$) m.currentTimeBetweenNetConnects% = m.timeBetweenNetConnects% m.networkTimerDownload = CreateObject("roAssociativeArray") m.networkTimerDownload.timerType = "TIMERTYPEPERIODIC" m.networkTimerDownload.timerInterval = m.timeBetweenNetConnects% if timeBetweenHeartbeats$ = "" then m.timeBetweenHearbeats% = 0 else m.timeBetweenHearbeats% = val(timeBetweenHeartbeats$) endif m.currentTimeBetweenHeartbeats% = m.timeBetweenHearbeats% m.diagnostics.PrintDebug("### Time between heartbeats = " + timeBetweenHeartbeats$) newTimer = CreateObject("roTimer") newTimer.SetPort(m.msgPort) m.networkTimerDownload.timer = newTimer ' get time range for when net connects can occur if contentDownloadsRestricted = "yes" then m.contentDownloadsRestricted = true m.contentDownloadRangeStart% = val(contentDownloadRangeStart) m.contentDownloadRangeLength% = val(contentDownloadRangeLength) m.diagnostics.PrintDebug("### Content downloads are restricted to the time from " + contentDownloadRangeStart + " for " + contentDownloadRangeLength + " minutes.") else m.diagnostics.PrintDebug("### Content downloads are unrestricted") m.contentDownloadsRestricted = false endif ' get time range for when heartbeats can occur if heartbeatsRestricted = "yes" then m.heartbeatsRestricted = true m.heartbeatsRangeStart% = val(heartbeatsRangeStart) m.heartbeatsRangeLength% = val(heartbeatsRangeLength) m.diagnostics.PrintDebug("### Heartbeats are restricted to the time from " + heartbeatsRangeStart + " for " + heartbeatsRangeLength + " minutes.") else m.diagnostics.PrintDebug("### Heartbeats are unrestricted") m.heartbeatsRestricted = false endif ' program the rate limit for networking if m.contentDownloadsRestricted then currentTime = m.systemTime.GetLocalDateTime() startOfRange% = m.contentDownloadRangeStart% endOfRange% = startOfRange% + m.contentDownloadRangeLength% notInDownloadWindow = OutOfDownloadWindow(currentTime, startOfRange%, endOfRange%) if notInDownloadWindow then wiredRL% = m.wiredRateLimits.rlOutsideWindow% wirelessRL% = m.wirelessRateLimits.rlOutsideWindow% else wiredRL% = m.wiredRateLimits.rlInWindow% wirelessRL% = m.wirelessRateLimits.rlInWindow% endif else wiredRL% = m.wiredRateLimits.rlOutsideWindow% wirelessRL% = m.wirelessRateLimits.rlOutsideWindow% endif ' diagnostic web server dwsParams = GetDWSParams(m.currentSync, m.bsp.registrySettings) dwsAA = CreateObject("roAssociativeArray") if dwsParams.dwsEnabled$ = "yes" then dwsAA["port"] = "80" dwsAA["password"] = dwsParams.dwsPassword$ endif SetDownloadRateLimit(m.diagnostics, 0, wiredRL%) if m.useWireless then SetDownloadRateLimit(m.diagnostics, 1, wirelessRL%) endif nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" dwsRebootRequired = nc.SetupDWS(dwsAA) if dwsRebootRequired then RebootSystem() endif return m.stRetrievingSyncList End Function Function GetDwsParams(syncSpec As Object, registrySettings As Object) dwsEnabled$ = syncSpec.LookupMetadata("client", "dwsEnabled") if dwsEnabled$ = "" then ' simple file networking case dwsEnabled$ = registrySettings.dwsEnabled$ dwsPassword$ = registrySettings.dwsPassword$ else dwsPassword$ = syncSpec.LookupMetadata("client", "dwsPassword") endif dwsParams = CreateObject("roAssociativeArray") dwsParams.dwsEnabled$ = dwsEnabled$ dwsParams.dwsPassword$ = dwsPassword$ return dwsParams End Function Function OutOfDownloadWindow(currentTime As Object, startOfRangeInMinutes% As Integer, endOfRangeInMinutes% As Integer) secondsPerDay% = 24 * 60 * 60 secondsSinceMidnight% = currentTime.GetHour() * 3600 + currentTime.GetMinute() * 60 + currentTime.GetSecond() startOfRangeInSeconds% = startOfRangeInMinutes% * 60 endOfRangeInSeconds% = endOfRangeInMinutes% * 60 notInDownloadWindow = false if endOfRangeInSeconds% <= secondsPerDay% then if not(secondsSinceMidnight% >= startOfRangeInSeconds% and secondsSinceMidnight% <= endOfRangeInSeconds%) then notInDownloadWindow = true endif else if not(((secondsSinceMidnight% >= startOfRangeInSeconds%) and (secondsSinceMidnight% < secondsPerDay%)) or (secondsSinceMidnight% < (endOfRangeInSeconds% - secondsPerDay%))) then notInDownloadWindow = true endif endif return notInDownloadWindow End Function Sub SetRateLimitValues(updateIfNotSpecified As boolean, rateLimits As Object, rateLimitModeOutsideWindow$ As String, rateLimitRateOutsideWindow$ As String, rateLimitModeInWindow$ As String, rateLimitRateInWindow$ As String) if rateLimitModeOutsideWindow$ = "unlimited" then rateLimits.rlOutsideWindow% = 0 else if rateLimitModeOutsideWindow$ = "specified" then if rateLimitRateOutsideWindow$ <> "" then rateLimits.rlOutsideWindow% = int(val(rateLimitRateOutsideWindow$)) endif else if updateIfNotSpecified or rateLimitModeOutsideWindow$ <> "" then rateLimits.rlOutsideWindow% = -1 endif if rateLimitModeInWindow$ = "unlimited" then rateLimits.rlInWindow% = 0 else if rateLimitModeInWindow$ = "specified" then if rateLimitRateInWindow$ <> "" then rateLimits.rlInWindow% = int(val(rateLimitRateInWindow$)) endif else if updateIfNotSpecified or rateLimitModeInWindow$ <> "" then rateLimits.rlInWindow% = -1 endif End Sub Function GetURL(base$ As String, urlFromSyncSpec$ As String) As String if instr(1, urlFromSyncSpec$, ":") > 0 then url$ = urlFromSyncSpec$ else if urlFromSyncSpec$ = "" then url$ = "" else url$ = base$ + urlFromSyncSpec$ endif return url$ End Function Sub AddUploadHeaders(url As Object, contentDisposition$) ' url.SetHeaders({}) url.SetHeaders(m.currentSync.GetMetadata("server")) ' Add device unique identifier, timezone url.AddHeader("DeviceID", m.deviceUniqueID$) url.AddHeader("DeviceModel", m.deviceModel$) url.AddHeader("DeviceFamily", m.deviceFamily$) url.AddHeader("DeviceFWVersion", m.firmwareVersion$) url.AddHeader("DeviceSWVersion", m.autorunVersion$) url.AddHeader("CustomAutorunVersion", m.customAutorunVersion$) url.AddHeader("utcTime", m.systemTime.GetUtcDateTime().GetString()) url.AddHeader("Content-Type", "application/octet-stream") url.AddHeader("Content-Disposition", contentDisposition$) End Sub Function GetContentDisposition(file As String) As String 'Content-Disposition: form-data; name="file"; filename="UploadPlaylog.xml" contentDisposition$ = "form-data; name=" contentDisposition$ = contentDisposition$ + chr(34) contentDisposition$ = contentDisposition$ + "file" contentDisposition$ = contentDisposition$ + chr(34) contentDisposition$ = contentDisposition$ + "; filename=" contentDisposition$ = contentDisposition$ + chr(34) contentDisposition$ = contentDisposition$ + file contentDisposition$ = contentDisposition$ + chr(34) return contentDisposition$ End Function Sub BuildFileDownloadList(syncSpec As Object) listOfDownloadFiles = syncSpec.GetFileList("download") fileInPoolStatus = m.bsp.assetPool.QueryFiles(syncSpec) m.filesToDownload = CreateObject("roAssociativeArray") m.chargeableFiles = CreateObject("roAssociativeArray") for each downloadFile in listOfDownloadFiles if not m.filesToDownload.DoesExist(downloadFile.hash) then fileToDownload = CreateObject("roAssociativeArray") fileToDownload.name = downloadFile.name fileToDownload.size = downloadFile.size fileToDownload.hash = downloadFile.hash fileToDownload.currentFilePercentage$ = "" fileToDownload.status$ = "" ' check to see if this file is already in the pool (and therefore doesn't need to be downloaded) if fileInPoolStatus.DoesExist(downloadFile.name) then fileInPool = fileInPoolStatus.Lookup(downloadFile.name) if fileInPool then fileToDownload.currentFilePercentage$ = "100" fileToDownload.status$ = "ok" endif endif m.filesToDownload.AddReplace(downloadFile.hash, fileToDownload) endif if IsString(downloadFile.chargeable) then if lcase(downloadFile.chargeable) = "yes" then m.chargeableFiles[downloadFile.name] = true endif endif next End Sub Sub PushDeviceDownloadProgressItem(fileItem As Object, type$ As String, currentFilePercentage$ As String, status$ As String) if type(fileItem) <> "roAssociativeArray" return deviceDownloadProgressItem = CreateObject("roAssociativeArray") deviceDownloadProgressItem.type$ = type$ deviceDownloadProgressItem.name$ = fileItem.name deviceDownloadProgressItem.hash$ = fileItem.hash deviceDownloadProgressItem.size$ = fileItem.size deviceDownloadProgressItem.currentFilePercentage$ = currentFilePercentage$ deviceDownloadProgressItem.status$ = status$ deviceDownloadProgressItem.utcTime$ = m.systemTime.GetUtcDateTime().GetString() if m.DeviceDownloadProgressItems.DoesExist(fileItem.name) existingDeviceDownloadProgressItem = m.DeviceDownloadProgressItems.Lookup(fileItem.name) deviceDownloadProgressItem.type$ = existingDeviceDownloadProgressItem.type$ endif m.DeviceDownloadProgressItems.AddReplace(fileItem.name, deviceDownloadProgressItem) End Sub Sub AddDeviceDownloadProgressItem(fileItem As Object, currentFilePercentage$ As String, status$ As String) if type(fileItem) <> "roAssociativeArray" return m.PushDeviceDownloadProgressItem(fileItem, "deviceDownloadProgressItem", currentFilePercentage$, status$) m.UploadDeviceDownloadProgressItems() End Sub Sub UploadDeviceDownloadProgressItems() if m.deviceDownloadProgressURL = "" then m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressItems - deviceDownloadProgressURL not set, return") return else m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressItems") endif ' verify that there is content to upload if m.DeviceDownloadProgressItems.IsEmpty() and m.DeviceDownloadProgressItemsPendingUpload.IsEmpty() then return ' create roUrlTransfer if needed if type(m.deviceDownloadProgressUploadURL) <> "roUrlTransfer" then m.deviceDownloadProgressUploadURL = CreateObject("roUrlTransfer") m.deviceDownloadProgressUploadURL.SetUrl(m.deviceDownloadProgressURL) m.deviceDownloadProgressUploadURL.SetPort(m.msgPort) m.deviceDownloadProgressUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.deviceDownloadProgressUploadURL.SetUrl(m.deviceDownloadProgressURL) then m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressItems - upload already in progress") return else m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressItems - proceed with post") end if ' merge new items into pending items for each deviceDownloadProgressItemKey in m.DeviceDownloadProgressItems deviceDownloadProgressItem = m.DeviceDownloadProgressItems.Lookup(deviceDownloadProgressItemKey) if m.DeviceDownloadProgressItemsPendingUpload.DoesExist(deviceDownloadProgressItem.name$) existingDeviceDownloadProgressItem = m.DeviceDownloadProgressItemsPendingUpload.Lookup(deviceDownloadProgressItem.name$) deviceDownloadProgressItem.type$ = existingDeviceDownloadProgressItem.type$ endif m.DeviceDownloadProgressItemsPendingUpload.AddReplace(deviceDownloadProgressItem.name$, deviceDownloadProgressItem) next ' generate the XML and upload the data root = CreateObject("roXMLElement") root.SetName("DeviceDownloadProgressItems") for each deviceDownloadProgressItemKey in m.DeviceDownloadProgressItemsPendingUpload deviceDownloadProgressItem = m.DeviceDownloadProgressItemsPendingUpload.Lookup(deviceDownloadProgressItemKey) BuildDeviceDownloadProgressItemXML(root, deviceDownloadProgressItem) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' prepare the upload contentDisposition$ = GetContentDisposition("UploadDeviceDownloadProgressItems.xml") m.AddUploadHeaders(m.deviceDownloadProgressUploadURL, contentDisposition$) m.deviceDownloadProgressUploadURL.AddHeader("updateDeviceLastDownload", "true") binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadDeviceDownloadProgressItems is " + stri(binding%)) ok = m.deviceDownloadProgressUploadURL.BindToInterface(binding%) if not ok then stop ok = m.deviceDownloadProgressUploadURL.AsyncPostFromString(xml) if not ok then m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressItems - AsyncPostFromString failed") endif m.DeviceDownloadProgressItems.Clear() End Sub Sub BuildDeviceDownloadProgressItemXML(root As Object, deviceDownloadProgressItem As Object) item = root.AddBodyElement() item.SetName(deviceDownloadProgressItem.type$) elem = item.AddElement("name") elem.SetBody(deviceDownloadProgressItem.name$) elem = item.AddElement("hash") elem.SetBody(deviceDownloadProgressItem.hash$) elem = item.AddElement("size") elem.SetBody(deviceDownloadProgressItem.size$) elem = item.AddElement("currentFilePercentage") elem.SetBody(deviceDownloadProgressItem.currentFilePercentage$) elem = item.AddElement("status") elem.SetBody(deviceDownloadProgressItem.status$) elem = item.AddElement("utcTime") elem.SetBody(deviceDownloadProgressItem.utcTime$) End Sub Sub UploadDeviceDownloadProgressFileList() if m.deviceDownloadProgressURL = "" then m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressFileList - deviceDownloadProgressURL not set, return") return else m.diagnostics.PrintDebug("### UploadDeviceDownloadProgressFileList") endif ' create roUrlTransfer if needed if type(m.deviceDownloadProgressUploadURL) <> "roUrlTransfer" then m.deviceDownloadProgressUploadURL = CreateObject("roUrlTransfer") m.deviceDownloadProgressUploadURL.SetUrl(m.deviceDownloadProgressURL) m.deviceDownloadProgressUploadURL.SetPort(m.msgPort) m.deviceDownloadProgressUploadURL.SetTimeout(900000) else ' cancel any uploads of this type that are in progress m.deviceDownloadProgressUploadURL.AsyncCancel() endif ' this data will overwrite any pending data so clear the existing data structures m.DeviceDownloadProgressItems.Clear() m.DeviceDownloadProgressItemsPendingUpload.Clear() ' create progress items for each file in the sync spec for each fileToDownloadKey in m.filesToDownload fileToDownload = m.filesToDownload.Lookup(fileToDownloadKey) m.PushDeviceDownloadProgressItem(fileToDownload, "fileInSyncSpec", fileToDownload.currentFilePercentage$, fileToDownload.status$) next m.UploadDeviceDownloadProgressItems() End Sub Sub AddDeviceDownloadItem(downloadEvent$ As String, fileName$ As String, downloadData$ As String) ' Make sure the array doesn't get too big. while m.DeviceDownloadItems.Count() > 100 m.DeviceDownloadItems.Shift() end while deviceDownloadItem = CreateObject("roAssociativeArray") deviceDownloadItem.downloadEvent$ = downloadEvent$ deviceDownloadItem.fileName$ = fileName$ deviceDownloadItem.downloadData$ = downloadData$ m.DeviceDownloadItems.push(deviceDownloadItem) m.UploadDeviceDownload() End Sub Sub UploadDeviceDownload() if m.deviceDownloadURL = "" then m.diagnostics.PrintDebug("### UploadDeviceDownload - deviceDownloadURL not set, return") return else m.diagnostics.PrintDebug("### UploadDeviceDownload") endif ' verify that there is content to upload if m.DeviceDownloadItems.Count() = 0 and m.DeviceDownloadItemsPendingUpload.Count() = 0 then return ' create roUrlTransfer if needed if type(m.deviceDownloadUploadURL) <> "roUrlTransfer" then m.deviceDownloadUploadURL = CreateObject("roUrlTransfer") m.deviceDownloadUploadURL.SetUrl(m.deviceDownloadURL) m.deviceDownloadUploadURL.SetPort(m.msgPort) m.deviceDownloadUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.deviceDownloadUploadURL.SetUrl(m.deviceDownloadURL) then m.diagnostics.PrintDebug("### UploadDeviceDownload - upload already in progress") if m.DeviceDownloadItemsPendingUpload.Count() > 100 then m.diagnostics.PrintDebug("### UploadDeviceDownload - clear pending items from queue") m.DeviceDownloadItemsPendingUpload.Clear() endif if m.DeviceDownloadItems.Count() > 100 then m.diagnostics.PrintDebug("### UploadDeviceDownload - clear items from queue") m.DeviceDownloadItems.Clear() endif return else m.diagnostics.PrintDebug("### UploadDeviceDownload - proceed with post") end if ' generate the XML and upload the data root = CreateObject("roXMLElement") root.SetName("DeviceDownloadBatch") ' first add the items that failed the last time for each deviceDownloadItem in m.DeviceDownloadItemsPendingUpload BuildDeviceDownloadItemXML(root, deviceDownloadItem) next ' now add the new items for each deviceDownloadItem in m.DeviceDownloadItems BuildDeviceDownloadItemXML(root, deviceDownloadItem) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' prepare the upload contentDisposition$ = GetContentDisposition("UploadDeviceDownload.xml") m.AddUploadHeaders(m.deviceDownloadUploadURL, contentDisposition$) binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadDeviceDownload is " + stri(binding%)) ok = m.deviceDownloadUploadURL.BindToInterface(binding%) if not ok then stop ok = m.deviceDownloadUploadURL.AsyncPostFromString(xml) if not ok then m.diagnostics.PrintDebug("### UploadDeviceDownload - AsyncPostFromString failed") endif for each deviceDownloadItem in m.DeviceDownloadItems m.DeviceDownloadItemsPendingUpload.push(deviceDownloadItem) next m.DeviceDownloadItems.Clear() End Sub Sub BuildDeviceDownloadItemXML(root As Object, deviceDownloadItem As Object) item = root.AddBodyElement() item.SetName("deviceDownload") elem = item.AddElement("downloadEvent") elem.SetBody(deviceDownloadItem.downloadEvent$) elem = item.AddElement("fileName") elem.SetBody(deviceDownloadItem.fileName$) elem = item.AddElement("downloadData") elem.SetBody(deviceDownloadItem.downloadData$) End Sub Sub UploadLogFiles() if m.uploadLogFileURL$ = "" then return ' create roUrlTransfer if needed if type(m.uploadLogFileURLXfer) <> "roUrlTransfer" then m.uploadLogFileURLXfer = CreateObject("roUrlTransfer") m.uploadLogFileURLXfer.SetUrl(m.uploadLogFileURL$) m.uploadLogFileURLXfer.SetPort(m.msgPort) m.uploadLogFileURLXfer.SetMinimumTransferRate(1,300) endif ' if a transfer is in progress, return m.diagnostics.PrintDebug("### Upload " + m.uploadLogFolder) if not m.uploadLogFileURLXfer.SetUrl(m.uploadLogFileURL$) then m.diagnostics.PrintDebug("### Upload " + m.uploadLogFolder + " - upload already in progress") return end if ' see if there are any files to upload listOfLogFiles = MatchFiles("/" + m.uploadLogFolder, "*.log") if listOfLogFiles.Count() = 0 then return binding% = GetBinding(m.bsp.logUploadsXfersEnabledWired, m.bsp.logUploadsXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadLogFiles is " + stri(binding%)) ok = m.uploadLogFileURLXfer.BindToInterface(binding%) ' upload the first file for each file in listOfLogFiles m.diagnostics.PrintDebug("### UploadLogFiles " + file + " to " + m.uploadLogFileURL$) fullFilePath = m.uploadLogFolder + "/" + file contentDisposition$ = GetContentDisposition(file) m.AddUploadHeaders(m.uploadLogFileURLXfer, contentDisposition$) ok = m.uploadLogFileURLXfer.AsyncPostFromFile(fullFilePath) if not ok then m.diagnostics.PrintDebug("### UploadLogFiles - AsyncPostFromFile failed") else m.logFileUpload = fullFilePath m.logFile$ = file return endif next End Sub Sub UploadLogFileHandler(msg As Object) if msg.GetResponseCode() = 200 then if IsString(m.logFileUpload) then m.diagnostics.PrintDebug("### UploadLogFile XferEvent - successfully uploaded " + m.logFileUpload) if m.enableLogDeletion then DeleteFile(m.logFileUpload) else target$ = m.uploadLogArchiveFolder + "/" + m.logFile$ ok = MoveFile(m.logFileUpload, target$) endif m.logFileUpload = invalid endif else if IsString(m.logFileUpload) then m.diagnostics.PrintDebug("### Failed to upload log file " + m.logFileUpload + ", error code = " + str(msg.GetResponseCode())) ' move file so that the script doesn't try to upload it again immediately target$ = m.uploadLogFailedFolder + "/" + m.logFile$ ok = MoveFile(m.logFileUpload, target$) endif m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_LOGFILE_UPLOAD_FAILURE, str(msg.GetResponseCode())) endif m.UploadLogFiles() End Sub Function UploadTrafficDownload(contentDownloaded# As Double) As Boolean if m.trafficDownloadURL = "" then m.diagnostics.PrintDebug("### UploadTrafficDownload - trafficDownloadURL not set, return") return false else m.diagnostics.PrintDebug("### UploadTrafficDownload") endif ' create roUrlTransfer if needed if type(m.trafficDownloadUploadURL) <> "roUrlTransfer" then m.trafficDownloadUploadURL = CreateObject("roUrlTransfer") m.trafficDownloadUploadURL.SetUrl(m.trafficDownloadURL) m.trafficDownloadUploadURL.SetPort(m.msgPort) m.trafficDownloadUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.trafficDownloadUploadURL.SetUrl(m.trafficDownloadURL) then m.diagnostics.PrintDebug("### UploadTrafficDownload - upload already in progress") return false end if m.lastContentDownloaded# = contentDownloaded# binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadTrafficDownload is " + stri(binding%)) ok = m.trafficDownloadUploadURL.BindToInterface(binding%) if not ok then stop return m.SendTrafficUpload(m.trafficDownloadUploadURL, contentDownloaded#, false) End Function Function UploadMRSSTrafficDownload(contentDownloaded# As Double) As Boolean if m.trafficDownloadURL = "" then m.diagnostics.PrintDebug("### UploadMRSSTrafficDownload - trafficDownloadURL not set, return") return false else m.diagnostics.PrintDebug("### UploadMRSSTrafficDownload") endif ' create roUrlTransfer if needed if type(m.mrssTrafficDownloadUploadURL) <> "roUrlTransfer" then m.mrssTrafficDownloadUploadURL = CreateObject("roUrlTransfer") m.mrssTrafficDownloadUploadURL.SetUrl(m.trafficDownloadURL) m.mrssTrafficDownloadUploadURL.SetPort(m.msgPort) m.mrssTrafficDownloadUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.mrssTrafficDownloadUploadURL.SetUrl(m.trafficDownloadURL) then m.diagnostics.PrintDebug("### UploadMRSSTrafficDownload - upload already in progress") totalContentDownloaded# = m.pendingMRSSContentDownloaded# totalContentDownloaded# = totalContentDownloaded# + contentDownloaded# m.pendingMRSSContentDownloaded# = totalContentDownloaded# return false end if contentDownloaded# = contentDownloaded# + m.pendingMRSSContentDownloaded# m.pendingMRSSContentDownloaded# = 0 m.lastMRSSContentDownloaded# = contentDownloaded# binding% = GetBinding(m.bsp.mediaFeedsXfersEnabledWired, m.bsp.mediaFeedsXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadMRSSTrafficDownload is " + stri(binding%)) ok = m.mrssTrafficDownloadUploadURL.BindToInterface(binding%) if not ok then stop m.diagnostics.PrintDebug("### UploadMRSSTrafficDownload: Content downloaded = " + str(contentDownloaded#)) return m.SendTrafficUpload(m.mrssTrafficDownloadUploadURL, contentDownloaded#, true) End Function Sub SendTrafficUpload(url As Object, contentDownloaded# As Double, intermediateTrafficReport As Boolean) As Boolean ' convert contentDownloaded# to contentDownloaded in KBytes which can be stored in an integer contentDownloaded% = contentDownloaded# / 1024 url.SetHeaders(m.currentSync.GetMetadata("server")) url.AddHeader("DeviceID", m.deviceUniqueID$) url.AddHeader("contentDownloadedInKBytes", StripLeadingSpaces(stri(contentDownloaded%))) url.AddHeader("DeviceFWVersion", m.firmwareVersion$) url.AddHeader("DeviceSWVersion", m.autorunVersion$) url.AddHeader("CustomAutorunVersion", m.customAutorunVersion$) url.AddHeader("timezone", m.systemTime.GetTimeZone()) url.AddHeader("utcTime", m.systemTime.GetUtcDateTime().GetString()) if intermediateTrafficReport then url.AddHeader("intermediateTrafficReport", "yes") endif ok = url.AsyncPostFromString("UploadTrafficDownload") if not ok then m.diagnostics.PrintDebug("### SendTrafficUpload - AsyncPostFromString failed") return false endif return ok End Sub Sub AddEventItem(eventType$ As String, eventData$ As String, eventResponseCode$ As String) ' Make sure the array doesn't get too big. while m.EventItems.Count() > 50 m.EventItems.Shift() end while eventItem = CreateObject("roAssociativeArray") eventItem.eventType$ = eventType$ eventItem.eventData$ = eventData$ eventItem.eventResponseCode$ = eventResponseCode$ m.EventItems.push(eventItem) m.UploadEvent() End Sub Sub AddDeviceErrorItem(event$ As String, name$ As String, failureReason$ As String, responseCode$ As String) ' Make sure the array doesn't get too big. while m.DeviceErrorItems.Count() > 50 m.DeviceErrorItems.Shift() end while deviceErrorItem = CreateObject("roAssociativeArray") deviceErrorItem.event$ = event$ deviceErrorItem.name$ = name$ deviceErrorItem.failureReason$ = failureReason$ deviceErrorItem.responseCode$ = responseCode$ m.DeviceErrorItems.push(deviceErrorItem) m.UploadDeviceError() End Sub Sub AddBatteryChargerItem(event$ As String, powerSource$ As String, batteryState$ As String, socPercentRange% As Integer) ' Make sure the array doesn't get too big. while m.BatteryChargerItems.Count() > 50 m.BatteryChargerItems.Shift() end while batteryChargerItem = CreateObject("roAssociativeArray") batteryChargerItem.event$ = event$ batteryChargerItem.powerSource$ = powerSource$ batteryChargerItem.batteryState$ = batteryState$ batteryChargerItem.socPercentRange$ = stri(socPercentRange%) m.BatteryChargerItems.push(batteryChargerItem) m.UploadBatteryCharger() End Sub Sub UploadBatteryCharger() m.diagnostics.PrintDebug("### UploadBatteryCharger") ' verify that there is content to upload if m.BatteryChargerItems.Count() = 0 then return ' create roUrlTransfer if needed if type(m.batteryChargerUploadURL) <> "roUrlTransfer" then m.batteryChargerUploadURL = CreateObject("roUrlTransfer") m.batteryChargerUploadURL.SetUrl(m.batteryChargerURL$) m.batteryChargerUploadURL.SetPort(m.msgPort) m.batteryChargerUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.batteryChargerUploadURL.SetUrl(m.batteryChargerURL$) then m.diagnostics.PrintDebug("### UploadBatteryCharger - upload already in progress") if m.BatteryChargerItems.Count() > 100 then m.diagnostics.PrintDebug("### UploadBatteryCharger - clear items from queue") m.BatteryChargerItems.Clear() endif return end if ' generate the XML and upload the data root = CreateObject("roXMLElement") root.SetName("BatteryChargerBatch") for each batteryChargerItem in m.BatteryChargerItems item = root.AddBodyElement() item.SetName("batteryCharger") elem = item.AddElement("event") elem.SetBody(batteryChargerItem.event$) elem = item.AddElement("powerSource") elem.SetBody(batteryChargerItem.powerSource$) elem = item.AddElement("batteryState") elem.SetBody(batteryChargerItem.batteryState$) elem = item.AddElement("socPercentRange") elem.SetBody(batteryChargerItem.socPercentRange$) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' prepare the upload contentDisposition$ = GetContentDisposition("UploadBatteryCharger.xml") m.AddUploadHeaders(m.batteryChargerUploadURL, contentDisposition$) binding% = GetBinding(m.bsp.healthXfersEnabledWired, m.bsp.healthXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for battery charger is " + stri(binding%)) ok = m.batteryChargerUploadURL.BindToInterface(binding%) if not ok then stop ok = m.batteryChargerUploadURL.AsyncPostFromString(xml) if not ok then m.diagnostics.PrintDebug("### UploadBatteryCharger - AsyncPostFromString failed") else ' clear out BatteryChargerItems - no big deal if the post fails m.BatteryChargerItems.Clear() endif End Sub Sub UploadEvent() m.diagnostics.PrintDebug("### UploadEvent") ' verify that there is content to upload if m.EventItems.Count() = 0 then return ' create roUrlTransfer if needed if type(m.eventUploadURL) <> "roUrlTransfer" then m.eventUploadURL = CreateObject("roUrlTransfer") m.eventUploadURL.SetUrl(m.eventURL) m.eventUploadURL.SetPort(m.msgPort) m.eventUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.eventUploadURL.SetUrl(m.eventURL) then m.diagnostics.PrintDebug("### UploadEvent - upload already in progress") if m.EventItems.Count() > 50 then m.diagnostics.PrintDebug("### UploadEvent - clear items from queue") m.EventItems.Clear() endif return end if ' generate the XML and upload the data root = CreateObject("roXMLElement") root.SetName("EventBatch") for each eventItem in m.EventItems item = root.AddBodyElement() item.SetName("event") elem = item.AddElement("eventType") elem.SetBody(eventItem.eventType$) elem = item.AddElement("eventData") elem.SetBody(eventItem.eventData$) elem = item.AddElement("eventResponseCode") elem.SetBody(eventItem.eventResponseCode$) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' prepare the upload contentDisposition$ = GetContentDisposition("UploadEvent.xml") m.AddUploadHeaders(m.eventUploadURL, contentDisposition$) binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadEvent is " + stri(binding%)) ok = m.eventUploadURL.BindToInterface(binding%) if not ok then stop ok = m.eventUploadURL.AsyncPostFromString(xml) if not ok then m.diagnostics.PrintDebug("### UploadEvent - AsyncPostFromString failed") else ' clear out EventItems - no big deal if the post fails m.EventItems.Clear() endif End Sub Sub UploadDeviceError() m.diagnostics.PrintDebug("### UploadDeviceError") ' verify that there is content to upload if m.DeviceErrorItems.Count() = 0 then return ' create roUrlTransfer if needed if type(m.deviceErrorUploadURL) <> "roUrlTransfer" then m.deviceErrorUploadURL = CreateObject("roUrlTransfer") m.deviceErrorUploadURL.SetUrl(m.deviceErrorURL) m.deviceErrorUploadURL.SetPort(m.msgPort) m.deviceErrorUploadURL.SetTimeout(900000) endif ' if a transfer is in progress, return if not m.deviceErrorUploadURL.SetUrl(m.deviceErrorURL) then m.diagnostics.PrintDebug("### UploadDeviceError - upload already in progress") if m.DeviceErrorItems.Count() > 50 then m.diagnostics.PrintDebug("### UploadDeviceError - clear items from queue") m.DeviceErrorItems.Clear() endif return end if ' generate the XML and upload the data root = CreateObject("roXMLElement") root.SetName("DeviceErrorBatch") for each deviceErrorItem in m.DeviceErrorItems item = root.AddBodyElement() item.SetName("deviceError") elem = item.AddElement("event") elem.SetBody(deviceErrorItem.event$) elem = item.AddElement("name") elem.SetBody(deviceErrorItem.name$) elem = item.AddElement("failureReason") elem.SetBody(deviceErrorItem.failureReason$) elem = item.AddElement("responseCode") elem.SetBody(deviceErrorItem.responseCode$) next xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) ' prepare the upload contentDisposition$ = GetContentDisposition("UploadDeviceError.xml") m.AddUploadHeaders(m.deviceErrorUploadURL, contentDisposition$) binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for UploadDeviceError is " + stri(binding%)) ok = m.deviceErrorUploadURL.BindToInterface(binding%) if not ok then stop ok = m.deviceErrorUploadURL.AsyncPostFromString(xml) if not ok then m.diagnostics.PrintDebug("### UploadDeviceError - AsyncPostFromString failed") else ' clear out DeviceErrorItems - no big deal if the post fails m.DeviceErrorItems.Clear() endif End Sub Function STNetworkSchedulerEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") currentTime = m.stateMachine.systemTime.GetLocalDateTime() ' set timer for when content download window starts / ends if m.stateMachine.contentDownloadsRestricted then startOfRange% = m.stateMachine.contentDownloadRangeStart% endOfRange% = startOfRange% + m.stateMachine.contentDownloadRangeLength% notInDownloadWindow = OutOfDownloadWindow(currentTime, startOfRange%, endOfRange%) if notInDownloadWindow then m.stateMachine.RestartContentDownloadWindowStartTimer(currentTime, startOfRange%) else m.stateMachine.RestartContentDownloadWindowEndTimer(currentTime, endOfRange%) endif endif ' set timer for when heartbeat window starts / ends if m.stateMachine.timeBetweenHearbeats% > 0 then if m.stateMachine.heartbeatsRestricted then startOfRange% = m.stateMachine.heartbeatsRangeStart% endOfRange% = startOfRange% + m.stateMachine.heartbeatsRangeLength% notInHeartbeatWindow = OutOfDownloadWindow(currentTime, startOfRange%, endOfRange%) if notInHeartbeatWindow then m.stateMachine.RestartHeartbeatsWindowStartTimer(currentTime, startOfRange%) else ' in window, send initial heartbeat m.stateMachine.SendHeartbeat() m.stateMachine.RestartHeartbeatsWindowEndTimer(currentTime, endOfRange%) endif else m.stateMachine.SendHeartbeat() endif endif return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") else if event["EventType"] = "DISK_ERROR" then errorEvent = event["DiskError"] m.stateMachine.AddDeviceErrorItem("diskError", errorEvent["source"] + " " + errorEvent["device"], errorEvent["error"], errorEvent["param"]) return "HANDLED" endif endif else if type(event) = "roTimerEvent" then if type(m.heartbeatTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.heartbeatTimer.GetIdentity()) then time = m.stateMachine.systemTime.GetLocalDateTime() if m.stateMachine.heartbeatsRestricted then startOfRange% = m.stateMachine.heartbeatsRangeStart% endOfRange% = startOfRange% + m.stateMachine.heartbeatsRangeLength% notInHeartbeatWindow = OutOfDownloadWindow(time, startOfRange%, endOfRange%) if not notInHeartbeatWindow then m.stateMachine.SendHeartbeat() endif else m.stateMachine.SendHeartbeat() endif return "HANDLED" endif endif if type(m.stateMachine.heartbeatsWindowStartTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.heartbeatsWindowStartTimer.GetIdentity()) then ' start window end timer if m.stateMachine.heartbeatsRestricted then currentTime = m.stateMachine.systemTime.GetLocalDateTime() startOfRange% = m.stateMachine.heartbeatsRangeStart% endOfRange% = startOfRange% + m.stateMachine.heartbeatsRangeLength% m.stateMachine.RestartHeartbeatsWindowEndTimer(currentTime, endOfRange%) endif return "HANDLED" endif endif if type(m.stateMachine.heartbeatsWindowEndTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.heartbeatsWindowEndTimer.GetIdentity()) then ' start window start timer currentTime = m.stateMachine.systemTime.GetLocalDateTime() startOfRange% = m.stateMachine.heartbeatsRangeStart% endOfRange% = startOfRange% + m.stateMachine.heartbeatsRangeLength% m.stateMachine.RestartHeartbeatsWindowStartTimer(currentTime, startOfRange%) return "HANDLED" endif endif if type(m.stateMachine.contentDownloadWindowStartTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.contentDownloadWindowStartTimer.GetIdentity()) then SetDownloadRateLimit(m.bsp.diagnostics, 0, m.stateMachine.wiredRateLimits.rlInWindow%) if m.stateMachine.useWireless then SetDownloadRateLimit(m.bsp.diagnostics, 1, m.stateMachine.wirelessRateLimits.rlInWindow%) endif ' start window end timer if m.stateMachine.contentDownloadsRestricted then currentTime = m.stateMachine.systemTime.GetLocalDateTime() startOfRange% = m.stateMachine.contentDownloadRangeStart% endOfRange% = startOfRange% + m.stateMachine.contentDownloadRangeLength% m.stateMachine.RestartContentDownloadWindowEndTimer(currentTime, endOfRange%) endif return "HANDLED" endif endif if type(m.stateMachine.contentDownloadWindowEndTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.contentDownloadWindowEndTimer.GetIdentity()) then ' send internal message to indicate that any in-progress sync pool downloads should stop cancelDownloadsEvent = CreateObject("roAssociativeArray") cancelDownloadsEvent["EventType"] = "CANCEL_DOWNLOADS" m.stateMachine.msgPort.PostMessage(cancelDownloadsEvent) ' change rate limit values - outside window SetDownloadRateLimit(m.bsp.diagnostics, 0, m.stateMachine.wiredRateLimits.rlOutsideWindow%) if m.stateMachine.useWireless then SetDownloadRateLimit(m.bsp.diagnostics, 1, m.stateMachine.wirelessRateLimits.rlOutsideWindow%) endif ' start window start timer currentTime = m.stateMachine.systemTime.GetLocalDateTime() startOfRange% = m.stateMachine.contentDownloadRangeStart% endOfRange% = startOfRange% + m.stateMachine.contentDownloadRangeLength% m.stateMachine.RestartContentDownloadWindowStartTimer(currentTime, startOfRange%) return "HANDLED" endif endif else if type(event) = "roUrlEvent" then if type (m.stateMachine.sendHeartbeatUrl) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.sendHeartbeatUrl.GetIdentity() then m.bsp.diagnostics.PrintDebug("### SendHeartbeatUrlEvent: " + stri(event.GetResponseCode())) if event.GetResponseCode() = 200 then m.stateMachine.numHeartbeatRetries% = 0 m.stateMachine.currentTimeBetweenHeartbeats% = m.stateMachine.timeBetweenHearbeats% else m.stateMachine.ResetHeartbeatTimerToDoRetry() endif ' start heartbeat timer if type(m.heartbeatTimer) <> "roTimer" then m.heartbeatTimer = CreateObject("roTimer") m.heartbeatTimer.SetPort(m.stateMachine.msgPort) endif m.heartbeatTimer.SetElapsed(m.stateMachine.currentTimeBetweenHeartbeats%, 0) m.heartbeatTimer.Start() return "HANDLED" endif endif if type (m.stateMachine.deviceDownloadUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.deviceDownloadUploadURL.GetIdentity() then if event.GetResponseCode() = 200 then m.stateMachine.DeviceDownloadItemsPendingUpload.Clear() else m.bsp.diagnostics.PrintDebug("### DeviceDownloadURLEvent: " + stri(event.GetResponseCode())) endif m.stateMachine.deviceDownloadUploadURL = invalid m.stateMachine.UploadDeviceDownload() return "HANDLED" endif endif if type (m.stateMachine.deviceDownloadProgressUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.deviceDownloadProgressUploadURL.GetIdentity() then if event.GetResponseCode() = 200 then m.stateMachine.DeviceDownloadProgressItemsPendingUpload.Clear() else m.bsp.diagnostics.PrintDebug("### DeviceDownloadProgressURLEvent: " + stri(event.GetResponseCode())) endif m.stateMachine.deviceDownloadProgressUploadURL = invalid m.stateMachine.UploadDeviceDownloadProgressItems() return "HANDLED" endif endif if type (m.stateMachine.uploadLogFileURLXfer) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.uploadLogFileURLXfer.GetIdentity() then m.stateMachine.uploadLogFileURLXfer = invalid m.stateMachine.UploadLogFileHandler(event) return "HANDLED" endif endif if type (m.stateMachine.eventUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.eventUploadURL.GetIdentity() then m.stateMachine.eventUploadURL = invalid m.stateMachine.UploadEvent() return "HANDLED" endif endif if type (m.stateMachine.deviceErrorUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.deviceErrorUploadURL.GetIdentity() then m.stateMachine.deviceErrorUploadURL = invalid m.stateMachine.UploadDeviceError() return "HANDLED" endif endif if type (m.stateMachine.batteryChargerUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.batteryChargerUploadURL.GetIdentity() then m.stateMachine.batteryChargerUploadURL = invalid m.stateMachine.UploadBatteryCharger() return "HANDLED" endif endif if type (m.stateMachine.trafficDownloadUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.trafficDownloadUploadURL.GetIdentity() then if event.GetInt() = m.URL_EVENT_COMPLETE then m.bsp.diagnostics.PrintDebug("### URLTrafficDownloadXferEvent: " + stri(event.GetResponseCode())) m.stateMachine.trafficDownloadUploadURL = invalid if event.GetResponseCode() <> 200 then m.stateMachine.UploadTrafficDownload(m.lastContentDownloaded#) endif endif return "HANDLED" endif endif if type (m.stateMachine.mrssTrafficDownloadUploadURL) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.mrssTrafficDownloadUploadURL.GetIdentity() then if event.GetInt() = m.URL_EVENT_COMPLETE then m.bsp.diagnostics.PrintDebug("### URLMRSSTrafficDownloadXferEvent: " + stri(event.GetResponseCode())) m.stateMachine.mrssTrafficDownloadUploadURL = invalid if event.GetResponseCode() <> 200 then m.stateMachine.UploadMRSSTrafficDownload(m.lastMRSSContentDownloaded#) endif endif return "HANDLED" endif endif endif stateData.nextState = m.superState return "SUPER" End Function Sub RestartWindowStartTimer(timer As Object, currentTime As Object, startOfRange% As Integer) hour% = startOfRange% / 60 minute% = startOfRange% - (hour% * 60) timeoutTime = CopyDateTime(currentTime) timeoutTime.SetHour(hour%) timeoutTime.SetMinute(minute%) timeoutTime.SetSecond(0) timeoutTime.SetMillisecond(0) GetNextTimeout(m.systemTime, timeoutTime) timer.SetDateTime(timeoutTime) timer.SetPort(m.msgPort) timer.Start() m.bsp.diagnostics.PrintDebug("RestartWindowStartTimer: set timer to start of window - " + timeoutTime.GetString()) End Sub Sub RestartContentDownloadWindowStartTimer(currentTime As Object, startOfRange% As Integer) m.contentDownloadWindowStartTimer = CreateObject("roTimer") m.RestartWindowStartTimer(m.contentDownloadWindowStartTimer, currentTime, startOfRange%) End Sub Sub RestartHeartbeatsWindowStartTimer(currentTime As Object, startOfRange% As Integer) m.heartbeatsWindowStartTimer = CreateObject("roTimer") m.RestartWindowStartTimer(m.heartbeatsWindowStartTimer, currentTime, startOfRange%) End Sub Function RestartWindowEndTimer(currentTime As Object, endOfRange% As Integer) As Object currentTime.SetHour(0) currentTime.SetMinute(0) currentTime.SetSecond(0) currentTime.SetMillisecond(0) currentTime.AddSeconds(endOfRange% * 60) currentTime.Normalize() GetNextTimeout(m.systemTime, currentTime) timer = CreateObject("roTimer") timer.SetDateTime(currentTime) timer.SetPort(m.msgPort) timer.Start() m.bsp.diagnostics.PrintDebug("RestartWindowEndTimer: set timer to end of window - " + currentTime.GetString()) return timer End Function Sub RestartContentDownloadWindowEndTimer(currentTime As Object, endOfRange% As Integer) m.contentDownloadWindowEndTimer = m.RestartWindowEndTimer(currentTime, endOfRange%) End Sub Sub RestartHeartbeatsWindowEndTimer(currentTime As Object, endOfRange% As Integer) m.heartbeatsWindowEndTimer = m.RestartWindowEndTimer(currentTime, endOfRange%) End Sub Sub GetNextTimeout(systemTime As Object, timerDateTime As object) As Object currentDateTime = systemTime.GetLocalDateTime() if timerDateTime.GetString() <= currentDateTime.GetString() then timerDateTime.AddSeconds(24 * 60 * 60) timerDateTime.Normalize() endif End Sub Sub WaitForTransfersToComplete() if type(m.trafficDownloadUploadURL) = "roUrlTransfer" then ' check to see if the trafficUpload call has been processed - if not, wait 5 seconds if not m.trafficDownloadUploadURL.SetUrl(m.trafficDownloadURL) then m.diagnostics.PrintDebug("### RebootAfterEventsSent - traffic upload still in progress - wait") sleep(5000) m.diagnostics.PrintDebug("### RebootAfterEventsSent - proceed after waiting 5 seconds for traffic upload to complete") else m.diagnostics.PrintDebug("### RebootAfterEventsSent - traffic upload must be complete - proceed") end if endif if type(m.deviceDownloadProgressUploadURL) = "roUrlTransfer" then ' check to see if the device download progress call has been processed - if not, wait 5 seconds if not m.deviceDownloadProgressUploadURL.SetUrl(m.deviceDownloadProgressURL) then sleep(5000) m.diagnostics.PrintDebug("### RebootAfterEventsSent - proceed after waiting 5 seconds for device download progress item upload to complete") else m.diagnostics.PrintDebug("### RebootAfterEventsSent - device download progress item upload must be complete - proceed") end if endif if type(m.deviceDownloadUploadURL) = "roUrlTransfer" then ' check to see if the device download call has been processed - if not, wait 5 seconds if not m.deviceDownloadUploadURL.SetUrl(m.deviceDownloadURL) then sleep(5000) m.diagnostics.PrintDebug("### RebootAfterEventsSent - proceed after waiting 5 seconds for device download upload to complete") else m.diagnostics.PrintDebug("### RebootAfterEventsSent - device download upload must be complete - proceed") end if endif End Sub Sub RebootAfterEventsSent() ' temporary sleep(2000) m.WaitForTransfersToComplete() m.UploadDeviceDownloadProgressItems() m.UploadDeviceDownload() m.WaitForTransfersToComplete() RebootSystem() End Sub Sub ResetDownloadTimerToDoRetry() if m.numRetries% >= m.maxRetries% then m.diagnostics.PrintDebug("### reset_download_timer_to_do_retry - max retries attempted - wait until next regularly scheduled download.") m.numRetries% = 0 m.currentTimeBetweenNetConnects% = m.timeBetweenNetConnects% else m.numRetries% = m.numRetries% + 1 m.currentTimeBetweenNetConnects% = m.retryInterval% * m.numRetries% m.diagnostics.PrintDebug("### reset_download_timer_to_do_retry - wait " + stri(m.currentTimeBetweenNetConnects%) + " seconds.") endif m.assetFetcher = invalid End Sub Sub ResetHeartbeatTimerToDoRetry() if m.numHeartbeatRetries% >= m.maxHeartbeatRetries% then m.diagnostics.PrintDebug("### reset_heartbeat_timer_to_do_retry - max retries attempted - wait until next regularly scheduled heartbeat.") m.numHeartbeatRetries% = 0 m.currentTimeBetweenHeartbeats% = m.timeBetweenHearbeats% else m.numHeartbeatRetries% = m.numHeartbeatRetries% + 1 m.currentTimeBetweenHeartbeats% = m.heartbeatRetryInterval% * m.numHeartbeatRetries% m.diagnostics.PrintDebug("### reset_heartbeat_timer_to_do_retry - wait " + stri(m.currentTimeBetweenHeartbeats%) + " seconds.") endif End Sub Function STWaitForTimeoutEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.stateMachine.networkTimerDownload.timer.SetElapsed(m.stateMachine.currentTimeBetweenNetConnects%, 0) m.stateMachine.networkTimerDownload.timer.Start() return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif else if type(event) = "roTimerEvent" then if type(m.stateMachine.networkTimerDownload.timer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.networkTimerDownload.timer.GetIdentity()) then stateData.nextState = m.stateMachine.stRetrievingSyncList return "TRANSITION" endif endif endif stateData.nextState = m.superState return "SUPER" End Function Function STRetrievingSyncListEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.StartSync("download") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif else if type(event) = "roUrlEvent" then m.bsp.diagnostics.PrintDebug("STRetrievingSyncListEventHandler: roUrlEvent") if stri(event.GetSourceIdentity()) = stri(m.xfer.GetIdentity()) then stateData.nextState = m.SyncSpecXferEvent(event) return "TRANSITION" endif endif stateData.nextState = m.superState return "SUPER" End Function Sub SendHeartbeat() if m.heartbeatURL$ = "" then m.bsp.diagnostics.PrintDebug("### SendHeartbeat - heartbeatURL not set, return") return endif m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### SendHeartbeat") m.sendHeartbeatUrl = CreateObject("roUrlTransfer") m.sendHeartbeatUrl.SetPort(m.msgPort) m.sendHeartbeatUrl.SetUrl(m.heartbeatURL$) m.sendHeartbeatUrl.SetTimeout(90000) ' 90 second timeout ' Add minimum number of headers m.sendHeartbeatUrl.AddHeader("act", m.currentSync.LookupMetadata("server", "account")) m.sendHeartbeatUrl.AddHeader("pwd", m.currentSync.LookupMetadata("server", "password")) m.sendHeartbeatUrl.AddHeader("g", m.currentSync.LookupMetadata("server", "group")) m.sendHeartbeatUrl.AddHeader("u", m.currentSync.LookupMetadata("server", "user")) m.sendHeartbeatUrl.AddHeader("id", m.deviceUniqueID$) m.sendHeartbeatUrl.AddHeader("model", m.deviceModel$) m.sendHeartbeatUrl.AddHeader("family", m.deviceFamily$) m.sendHeartbeatUrl.AddHeader("fw", m.firmwareVersion$) m.sendHeartbeatUrl.AddHeader("sw", m.autorunVersion$) m.sendHeartbeatUrl.AddHeader("ca", m.customAutorunVersion$) m.sendHeartbeatUrl.AddHeader("tz", m.systemTime.GetTimeZone()) ' card size du = CreateObject("roStorageInfo", "./") m.sendHeartbeatUrl.AddHeader("sz", str(du.GetSizeInMegabytes())) du = invalid ' presentation name if type(m.bsp.sign) = "roAssociativeArray" then m.sendHeartbeatUrl.AddHeader("pName", m.bsp.sign.name$) else m.sendHeartbeatUrl.AddHeader("pName", "none") endif binding% = GetBinding(m.bsp.healthXfersEnabledWired, m.bsp.healthXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for Heartbeat is " + stri(binding%)) ok = m.sendHeartbeatUrl.BindToInterface(binding%) if not ok then stop if not m.sendHeartbeatUrl.AsyncGetToString() then stop End Sub Sub StartSync(syncType$ As String) ' Call when you want to start a sync operation m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### start_sync " + syncType$) if type(m.stateMachine.assetFetcher) = "roAssetFetcher" then ' This should be improved in the future to work out ' whether the sync spec we're currently satisfying ' matches the one that we're currently downloading or ' not. m.bsp.diagnostics.PrintDebug("### sync already active so we'll let it continue") m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNC_ALREADY_ACTIVE, "") return endif if syncType$ = "cache" then if not m.stateMachine.proxy_mode then m.bsp.diagnostics.PrintDebug("### cache download requested but the BrightSign is not configured to use a cache server") return endif endif m.xfer = CreateObject("roUrlTransfer") m.xfer.SetPort(m.stateMachine.msgPort) m.stateMachine.syncType$ = syncType$ m.bsp.diagnostics.PrintDebug("### xfer created - identity = " + stri(m.xfer.GetIdentity()) + " ###") ' We've read in our current sync. Talk to the server to get ' the next sync. Note that we use the current-sync.xml because ' we need to tell the server what we are _currently_ running not ' what we might be running at some point in the future. base$ = m.stateMachine.currentSync.LookupMetadata("client", "base") nextURL = GetURL(base$, m.stateMachine.currentSync.LookupMetadata("client", "next")) m.bsp.diagnostics.PrintDebug("### Looking for new sync list from " + nextURL) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_CHECK_CONTENT, nextURL) m.xfer.SetUrl(nextURL) if m.stateMachine.setUserAndPassword then m.xfer.SetUserAndPassword(m.stateMachine.user$, m.stateMachine.password$) m.xfer.EnableUnsafeAuthentication(m.stateMachine.enableUnsafeAuthentication) m.xfer.SetMinimumTransferRate(10,240) m.xfer.SetHeaders(m.stateMachine.currentSync.GetMetadata("server")) ' Add presentation name to header if type(m.bsp.sign) = "roAssociativeArray" then m.xfer.AddHeader("presentationName", m.bsp.sign.name$) else m.xfer.AddHeader("presentationName", "none") endif ' Add device unique identifier, timezone m.xfer.AddHeader("DeviceID", m.stateMachine.deviceUniqueID$) m.xfer.AddHeader("DeviceModel", m.stateMachine.deviceModel$) m.xfer.AddHeader("DeviceFamily", m.stateMachine.deviceFamily$) m.xfer.AddHeader("DeviceFWVersion", m.stateMachine.firmwareVersion$) m.xfer.AddHeader("DeviceSWVersion", m.stateMachine.autorunVersion$) m.xfer.AddHeader("CustomAutorunVersion", m.stateMachine.customAutorunVersion$) m.xfer.AddHeader("timezone", m.stateMachine.systemTime.GetTimeZone()) m.xfer.AddHeader("localTime", m.stateMachine.systemTime.GetLocalDateTime().GetString()) m.stateMachine.AddMiscellaneousHeaders(m.xfer, m.bsp.assetPool) ' Add headers if this is a local system trying to connect to BSN for the first time localToBSN = false if not FileExists("current-sync.xml") then if not FileExists("local-sync.xml") then if FileExists("localToBSN-sync.xml") then m.stateMachine.AddLocalToBSNHeaders(m.xfer) endif endif endif binding% = GetBinding(m.bsp.contentXfersEnabledWired, m.bsp.contentXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for StartSync is " + stri(binding%)) ok = m.xfer.BindToInterface(binding%) if not ok then stop if not m.xfer.AsyncGetToObject("roSyncSpec") then stop End Sub Sub AddLocalToBSNHeaders(xfer As Object) xfer.AddHeader("setup", "yes") xfer.AddHeader("synclistid", "0") xfer.AddHeader("unitName", m.bsp.registrySettings.unitName$) xfer.AddHeader("unitNamingMethod", m.bsp.registrySettings.unitNamingMethod$) xfer.AddHeader("unitDescription", m.bsp.registrySettings.unitDescription$) xfer.AddHeader("playbackLoggingEnabled", m.bsp.registrySettings.playbackLoggingEnabled) xfer.AddHeader("eventLoggingEnabled", m.bsp.registrySettings.eventLoggingEnabled) xfer.AddHeader("diagnosticLoggingEnabled", m.bsp.registrySettings.diagnosticLoggingEnabled) xfer.AddHeader("stateLoggingEnabled", m.bsp.registrySettings.stateLoggingEnabled) xfer.AddHeader("uploadLogFilesAtBoot", m.bsp.registrySettings.uploadLogFilesAtBoot) xfer.AddHeader("uploadLogFilesAtSpecificTime", m.bsp.registrySettings.uploadLogFilesAtSpecificTime) xfer.AddHeader("uploadLogFilesTime", m.bsp.registrySettings.uploadLogFilesTime$) xfer.AddHeader("timeBetweenNetConnects", m.bsp.registrySettings.timeBetweenNetConnects$) xfer.AddHeader("contentDownloadsRestricted", m.bsp.registrySettings.contentDownloadsRestricted) if m.bsp.registrySettings.contentDownloadsRestricted = "yes" then xfer.AddHeader("contentDownloadRangeStart", m.bsp.registrySettings.contentDownloadRangeStart) xfer.AddHeader("contentDownloadRangeLength", m.bsp.registrySettings.contentDownloadRangeLength) endif xfer.AddHeader("timeBetweenHeartbeats", m.bsp.registrySettings.timeBetweenHeartbeats$) xfer.AddHeader("heartbeatsRestricted", m.bsp.registrySettings.heartbeatsRestricted) if m.bsp.registrySettings.heartbeatsRestricted = "yes" then xfer.AddHeader("heartbeatsRangeStart", m.bsp.registrySettings.heartbeatsRangeStart) xfer.AddHeader("heartbeatsRangeLength", m.bsp.registrySettings.heartbeatsRangeLength) endif xfer.AddHeader("dwsEnabled", m.bsp.registrySettings.dwsEnabled$) xfer.AddHeader("dwsPassword", m.bsp.registrySettings.dwsPassword$) xfer.AddHeader("proxy", m.bsp.registrySettings.proxy$) xfer.AddHeader("useWireless", m.bsp.registrySettings.useWireless$) if m.bsp.registrySettings.useWireless$ = "yes" then xfer.AddHeader("ssid", m.bsp.registrySettings.ssid$) xfer.AddHeader("passphrase", m.bsp.registrySettings.passphrase$) endif xfer.AddHeader("timeServer", m.bsp.registrySettings.timeServer$) ' first network configuration useDHCP$ = m.bsp.registrySection.Read("dhcp") xfer.AddHeader("useDHCP", useDHCP$) if useDHCP$ = "no" then xfer.AddHeader("staticIPAddress", m.bsp.registrySection.Read("sip")) xfer.AddHeader("subnetMask", m.bsp.registrySection.Read("sm")) xfer.AddHeader("broadcast", m.bsp.registrySection.Read("gw")) xfer.AddHeader("gateway", m.bsp.registrySection.Read("bc")) xfer.AddHeader("dns1", m.bsp.registrySection.Read("d1")) xfer.AddHeader("dns2", m.bsp.registrySection.Read("d2")) xfer.AddHeader("dns3", m.bsp.registrySection.Read("d3")) endif ' rate limit parameters xfer.AddHeader("rateLimitModeOutsideWindow", m.bsp.registrySettings.rateLimitModeOutsideWindow$) xfer.AddHeader("rateLimitRateOutsideWindow", m.bsp.registrySettings.rateLimitRateOutsideWindow$) xfer.AddHeader("rateLimitModeInWindow", m.bsp.registrySettings.rateLimitModeInWindow$) xfer.AddHeader("rateLimitRateInWindow", m.bsp.registrySettings.rateLimitRateInWindow$) xfer.AddHeader("rateLimitModeInitialDownloads", m.bsp.registrySettings.rateLimitModeInitialDownloads$) xfer.AddHeader("rateLimitRateInitialDownloads", m.bsp.registrySettings.rateLimitRateInitialDownloads$) ' second network configuration useDHCP_2$ = m.bsp.registrySection.Read("dhcp2") if useDHCP_2$ = "" then useDHCP_2$ = "yes" endif xfer.AddHeader("useDHCP_2", useDHCP$) if useDHCP$ = "no" then xfer.AddHeader("staticIPAddress_2", m.bsp.registrySection.Read("sip2")) xfer.AddHeader("subnetMask_2", m.bsp.registrySection.Read("sm2")) xfer.AddHeader("broadcast_2", m.bsp.registrySection.Read("gw2")) xfer.AddHeader("gateway_2", m.bsp.registrySection.Read("bc2")) xfer.AddHeader("dns1_2", m.bsp.registrySection.Read("d12")) xfer.AddHeader("dns2_2", m.bsp.registrySection.Read("d22")) xfer.AddHeader("dns3_2", m.bsp.registrySection.Read("d32")) endif xfer.AddHeader("rateLimitModeOutsideWindow_2", m.bsp.registrySettings.rateLimitModeOutsideWindow_2$) xfer.AddHeader("rateLimitRateOutsideWindow_2", m.bsp.registrySettings.rateLimitRateOutsideWindow_2$) xfer.AddHeader("rateLimitModeInWindow_2", m.bsp.registrySettings.rateLimitModeInWindow_2$) xfer.AddHeader("rateLimitRateInWindow_2", m.bsp.registrySettings.rateLimitRateInWindow_2$) xfer.AddHeader("rateLimitModeInitialDownloads_2", m.bsp.registrySettings.rateLimitModeInitialDownloads_2$) xfer.AddHeader("rateLimitRateInitialDownloads_2", m.bsp.registrySettings.rateLimitRateInitialDownloads_2$) xfer.AddHeader("networkConnectionPriorityWired", m.bsp.registrySettings.networkConnectionPriorityWired$) xfer.AddHeader("contentXfersEnabledWired", m.bsp.registrySettings.contentXfersEnabledWired$) xfer.AddHeader("textFeedsXfersEnabledWired", m.bsp.registrySettings.textFeedsXfersEnabledWired$) xfer.AddHeader("healthXfersEnabledWired", m.bsp.registrySettings.healthXfersEnabledWired$) xfer.AddHeader("mediaFeedsXfersEnabledWired", m.bsp.registrySettings.mediaFeedsXfersEnabledWired$) xfer.AddHeader("logUploadsXfersEnabledWired", m.bsp.registrySettings.logUploadsXfersEnabledWired$) if m.bsp.registrySettings.useWireless$ = "yes" then xfer.AddHeader("networkConnectionPriorityWireless", m.bsp.registrySettings.networkConnectionPriorityWireless$) xfer.AddHeader("contentXfersEnabledWireless", m.bsp.registrySettings.contentXfersEnabledWireless$) xfer.AddHeader("textFeedsXfersEnabledWireless", m.bsp.registrySettings.textFeedsXfersEnabledWireless$) xfer.AddHeader("healthXfersEnabledWireless", m.bsp.registrySettings.healthXfersEnabledWireless$) xfer.AddHeader("mediaFeedsXfersEnabledWireless", m.bsp.registrySettings.mediaFeedsXfersEnabledWireless$) xfer.AddHeader("logUploadsXfersEnabledWireless", m.bsp.registrySettings.logUploadsXfersEnabledWireless$) endif End Sub Sub AddMiscellaneousHeaders(urlXfer As Object, assetPool As Object) ' Add card size du = CreateObject("roStorageInfo", "./") urlXfer.AddHeader("storage-size", str(du.GetSizeInMegabytes())) urlXfer.AddHeader("storage-fs", du.GetFileSystemType()) ' Add estimated realized size tempRealizer = CreateObject("roAssetRealizer", assetPool, "/") tempSpec = CreateObject("roSyncSpec") if tempSpec.ReadFromFile("current-sync.xml") or tempSpec.ReadFromFile("localToBSN-sync.xml") then urlXfer.AddHeader("storage-current-used", str(tempRealizer.EstimateRealizedSizeInMegabytes(tempSpec))) endif tempRealizer = invalid tempSpec = invalid End Sub Function SyncSpecXferEvent(event As Object) As Object nextState = invalid xferInUse = false if event.GetResponseCode() = 200 then m.stateMachine.newSync = event.GetObject() m.bsp.diagnostics.PrintDebug("### Spec received from server") ' check for a forced reboot forceReboot$ = LCase(m.stateMachine.newSync.LookupMetadata("client", "forceReboot")) if forceReboot$ = "true" then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "FORCE REBOOT") m.stateMachine.logging.FlushLogFile() a=RebootSystem() stop endif ' check for forced log upload forceLogUpload$ = LCase(m.stateMachine.newSync.LookupMetadata("client", "forceLogUpload")) if forceLogUpload$ = "true" then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "FORCE LOG UPLOAD") m.stateMachine.logging.CutoverLogFile(true) m.bsp.LogActivePresentation() endif if m.stateMachine.newSync.EqualTo(m.stateMachine.currentSync) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "NO") m.bsp.diagnostics.PrintDebug("### Server has given us a spec that matches current-sync. Nothing more to do.") m.stateMachine.AddDeviceDownloadItem("SyncSpecUnchanged", "", "") m.stateMachine.newSync = invalid m.stateMachine.numRetries% = 0 m.stateMachine.currentTimeBetweenNetConnects% = m.stateMachine.timeBetweenNetConnects% ' if necessary, upload the list of current files to the server if m.stateMachine.FileListPendingUpload then m.stateMachine.BuildFileDownloadList(m.stateMachine.currentSync) m.stateMachine.UploadDeviceDownloadProgressFileList() m.stateMachine.FileListPendingUpload = false endif return m.stateMachine.stWaitForTimeout endif badReadySync = CreateObject("roSyncSpec") if badReadySync.ReadFromFile("bad-sync.xml") then if m.stateMachine.newSync.EqualTo(badReadySync) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "BAD SYNC") m.bsp.diagnostics.PrintDebug("### Server has given us a spec that matches bad-sync. Nothing more to do.") badReadySync = invalid m.stateMachine.newSync = invalid m.stateMachine.numRetries% = 0 m.stateMachine.currentTimeBetweenNetConnects% = m.stateMachine.timeBetweenNetConnects% return m.stateMachine.stWaitForTimeout endif endif m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_RECEIVED, "YES") m.stateMachine.assetCollection = m.stateMachine.newSync.GetAssets("download") m.stateMachine.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.assetPool, m.stateMachine.assetCollection) m.stateMachine.BuildFileDownloadList(m.stateMachine.newSync) m.stateMachine.UploadDeviceDownloadProgressFileList() m.stateMachine.FileListPendingUpload = false m.stateMachine.AddDeviceDownloadItem("SyncSpecDownloadStarted", "", "") m.stateMachine.contentDownloaded# = 0 ' Update the pool sizes based on the newly downloaded sync spec m.bsp.SetPoolSizes( m.stateMachine.newSync ) ' Retrieve network connection priorities from the sync spec networkConnectionPriorityWired$ = m.stateMachine.newSync.LookupMetadata("client", "networkConnectionPriorityWired") if networkConnectionPriorityWired$ <> "" then networkConnectionPriorityWired% = int(val(networkConnectionPriorityWired$)) nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then nc.SetRoutingMetric(networkConnectionPriorityWired%) nc.Apply() nc = invalid endif endif networkConnectionPriorityWireless$ = m.stateMachine.newSync.LookupMetadata("client", "networkConnectionPriorityWireless") if networkConnectionPriorityWireless$ <> "" then networkConnectionPriorityWireless% = int(val(networkConnectionPriorityWireless$)) nc = CreateObject("roNetworkConfiguration", 1) if type(nc) = "roNetworkConfiguration" then nc.SetRoutingMetric(networkConnectionPriorityWireless%) nc.Apply() nc = invalid endif endif ' Retrieve data xfers enabled information from the sync spec and write to registry if it's changed m.bsp.contentXfersEnabledWired = GetDataTransferEnabled(m.statemachine.newSync, "contentXfersEnabledWired") m.bsp.registrySettings.contentXfersEnabledWired$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "contentXfersEnabledWired"), m.bsp.registrySettings.contentXfersEnabledWired$, "cwr") m.bsp.textFeedsXfersEnabledWired = GetDataTransferEnabled(m.statemachine.newSync, "textFeedsXfersEnabledWired") m.bsp.registrySettings.textFeedsXfersEnabledWired$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "textFeedsXfersEnabledWired"), m.bsp.registrySettings.textFeedsXfersEnabledWired$, "twr") m.bsp.healthXfersEnabledWired = GetDataTransferEnabled(m.statemachine.newSync, "healthXfersEnabledWired") m.bsp.registrySettings.healthXfersEnabledWired$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "healthXfersEnabledWired"), m.bsp.registrySettings.healthXfersEnabledWired$, "hwr") m.bsp.mediaFeedsXfersEnabledWired = GetDataTransferEnabled(m.statemachine.newSync, "mediaFeedsXfersEnabledWired") m.bsp.registrySettings.mediaFeedsXfersEnabledWired$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "mediaFeedsXfersEnabledWired"), m.bsp.registrySettings.mediaFeedsXfersEnabledWired$, "mwr") m.bsp.logUploadsXfersEnabledWired = GetDataTransferEnabled(m.statemachine.newSync, "logUploadsXfersEnabledWired") m.bsp.registrySettings.logUploadsXfersEnabledWired$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "logUploadsXfersEnabledWired"), m.bsp.registrySettings.logUploadsXfersEnabledWired$, "lwr") m.bsp.contentXfersEnabledWireless = GetDataTransferEnabled(m.statemachine.newSync, "contentXfersEnabledWireless") m.bsp.registrySettings.contentXfersEnabledWireless$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "contentXfersEnabledWireless"), m.bsp.registrySettings.contentXfersEnabledWireless$, "cwf") m.bsp.textFeedsXfersEnabledWireless = GetDataTransferEnabled(m.statemachine.newSync, "textFeedsXfersEnabledWireless") m.bsp.registrySettings.textFeedsXfersEnabledWireless$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "textFeedsXfersEnabledWireless"), m.bsp.registrySettings.textFeedsXfersEnabledWireless$, "twf") m.bsp.healthXfersEnabledWireless = GetDataTransferEnabled(m.statemachine.newSync, "healthXfersEnabledWireless") m.bsp.registrySettings.healthXfersEnabledWireless$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "healthXfersEnabledWireless"), m.bsp.registrySettings.healthXfersEnabledWireless$, "hwf") m.bsp.mediaFeedsXfersEnabledWireless = GetDataTransferEnabled(m.statemachine.newSync, "mediaFeedsXfersEnabledWireless") m.bsp.registrySettings.mediaFeedsXfersEnabledWireless$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "mediaFeedsXfersEnabledWireless"), m.bsp.registrySettings.mediaFeedsXfersEnabledWireless$, "mwf") m.bsp.logUploadsXfersEnabledWireless = GetDataTransferEnabled(m.statemachine.newSync, "logUploadsXfersEnabledWireless") m.bsp.registrySettings.logUploadsXfersEnabledWireless$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "logUploadsXfersEnabledWireless"), m.bsp.registrySettings.logUploadsXfersEnabledWireless$, "lwf") ' Retrieve logging information from the sync spec playbackLoggingEnabled = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "playbackLoggingEnabled")) if b$ = "yes" then playbackLoggingEnabled = true eventLoggingEnabled = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "eventLoggingEnabled")) if b$ = "yes" then eventLoggingEnabled = true diagnosticLoggingEnabled = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "diagnosticLoggingEnabled")) if b$ = "yes" then diagnosticLoggingEnabled = true stateLoggingEnabled = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "stateLoggingEnabled")) if b$ = "yes" then stateLoggingEnabled = true uploadLogFilesAtBoot = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "uploadLogFilesAtBoot")) if b$ = "yes" then uploadLogFilesAtBoot = true uploadLogFilesAtSpecificTime = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "uploadLogFilesAtSpecificTime")) if b$ = "yes" then uploadLogFilesAtSpecificTime = true uploadLogFilesTime% = 0 uploadLogFilesTime$ = m.statemachine.newSync.LookupMetadata("client", "uploadLogFilesTime") if uploadLogFilesTime$ <> "" then uploadLogFilesTime% = int(val(uploadLogFilesTime$)) m.stateMachine.logging.ReinitializeLogging(playbackLoggingEnabled, eventLoggingEnabled, stateLoggingEnabled, diagnosticLoggingEnabled, uploadLogFilesAtBoot, uploadLogFilesAtSpecificTime, uploadLogFilesTime%) ' Retrieve unit name, description from sync spec and write to registry if it's changed unitNameFromRegistry$ = m.bsp.registrySettings.unitName$ unitNamingMethodFromRegistry$ = m.bsp.registrySettings.unitNamingMethod$ unitDescriptionFromRegistry$ = m.bsp.registrySettings.unitDescription$ unitName$ = m.stateMachine.newSync.LookupMetadata("client", "unitName") unitNamingMethod$ = m.stateMachine.newSync.LookupMetadata("client", "unitNamingMethod") unitDescription$ = m.stateMachine.newSync.LookupMetadata("client", "unitDescription") if unitName$ <> unitNameFromRegistry$ then m.bsp.WriteRegistrySetting("un", unitName$) m.bsp.registrySettings.unitName$ = unitName$ endif if unitNamingMethod$ <> unitNamingMethodFromRegistry$ then m.bsp.WriteRegistrySetting("unm", unitNamingMethod$) m.bsp.registrySettings.unitNamingMethod$ = unitNamingMethod$ endif if unitDescription$ <> unitDescriptionFromRegistry$ then m.bsp.WriteRegistrySetting("ud", unitDescription$) m.bsp.registrySettings.unitDescription$ = unitDescription$ endif ' Retrieve latest network configuration information from sync spec useWireless$ = m.stateMachine.newSync.LookupMetadata("client", "useWireless") if not m.stateMachine.modelSupportsWifi then useWireless$ = "no" if useWireless$ = "yes" then m.stateMachine.useWireless = true ssid$ = m.stateMachine.newSync.LookupMetadata("client", "ssid") passphrase$ = m.stateMachine.newSync.LookupMetadata("client", "passphrase") else m.stateMachine.useWireless = false endif timeServer$ = m.stateMachine.newSync.LookupMetadata("client", "timeServer") ' if useDHCP is not set, don't touch the networking configuration (likely that the BrightSign is using simple networking) useDHCP = m.stateMachine.newSync.LookupMetadata("client", "useDHCP") if useDHCP <> "" then wiredNetworkingParameters = {} wiredNetworkingParameters.networkConfigurationIndex% = 0 wiredNetworkingParameters.networkConnectionPriority$ = m.stateMachine.newSync.LookupMetadata("client", "networkConnectionPriorityWired") if m.stateMachine.useWireless then wirelessNetworkingParameters = {} wirelessNetworkingParameters.networkConfigurationIndex% = 1 wirelessNetworkingParameters.networkConnectionPriority$ = m.stateMachine.newSync.LookupMetadata("client", "networkConnectionPriorityWireless") if useDHCP <> "" then wirelessNetworkingParameters.useDHCP$ = m.stateMachine.newSync.LookupMetadata("client", "useDHCP") if useDHCP = "no" then wirelessNetworkingParameters.staticIPAddress$ = m.stateMachine.newSync.LookupMetadata("client", "staticIPAddress") wirelessNetworkingParameters.subnetMask$ = m.stateMachine.newSync.LookupMetadata("client", "subnetMask") wirelessNetworkingParameters.gateway$ = m.stateMachine.newSync.LookupMetadata("client", "gateway") wirelessNetworkingParameters.dns1$ = m.stateMachine.newSync.LookupMetadata("client", "dns1") wirelessNetworkingParameters.dns2$ = m.stateMachine.newSync.LookupMetadata("client", "dns2") wirelessNetworkingParameters.dns3$ = m.stateMachine.newSync.LookupMetadata("client", "dns3") endif wirelessNetworkingParameters.useWireless = true wirelessNetworkingParameters.ssid$ = ssid$ wirelessNetworkingParameters.passphrase$ = passphrase$ wirelessNetworkingParameters.timeServer$ = timeServer$ m.ConfigureNetwork(wirelessNetworkingParameters, m.bsp.registrySettings.wirelessNetworkingParameters, "") endif wiredNetworkingParameters.useDHCP$ = m.stateMachine.newSync.LookupMetadata("client", "useDHCP_2") if wiredNetworkingParameters.useDHCP$ = "no" then wiredNetworkingParameters.staticIPAddress$ = m.stateMachine.newSync.LookupMetadata("client", "staticIPAddress_2") wiredNetworkingParameters.subnetMask$ = m.stateMachine.newSync.LookupMetadata("client", "subnetMask_2") wiredNetworkingParameters.gateway$ = m.stateMachine.newSync.LookupMetadata("client", "gateway_2") wiredNetworkingParameters.dns1$ = m.stateMachine.newSync.LookupMetadata("client", "dns1_2") wiredNetworkingParameters.dns2$ = m.stateMachine.newSync.LookupMetadata("client", "dns2_2") wiredNetworkingParameters.dns3$ = m.stateMachine.newSync.LookupMetadata("client", "dns3_2") endif wiredNetworkingParameters.useWireless = false wiredNetworkingParameters.timeServer$ = timeServer$ m.ConfigureNetwork(wiredNetworkingParameters, m.bsp.registrySettings.wiredNetworkingParameters, "2") else wiredNetworkingParameters.useDHCP$ = m.stateMachine.newSync.LookupMetadata("client", "useDHCP") if wiredNetworkingParameters.useDHCP$ = "no" then wiredNetworkingParameters.staticIPAddress$ = m.stateMachine.newSync.LookupMetadata("client", "staticIPAddress") wiredNetworkingParameters.subnetMask$ = m.stateMachine.newSync.LookupMetadata("client", "subnetMask") wiredNetworkingParameters.gateway$ = m.stateMachine.newSync.LookupMetadata("client", "gateway") wiredNetworkingParameters.dns1$ = m.stateMachine.newSync.LookupMetadata("client", "dns1") wiredNetworkingParameters.dns2$ = m.stateMachine.newSync.LookupMetadata("client", "dns2") wiredNetworkingParameters.dns3$ = m.stateMachine.newSync.LookupMetadata("client", "dns3") endif wiredNetworkingParameters.useWireless = false wiredNetworkingParameters.timeServer$ = timeServer$ m.ConfigureNetwork(wiredNetworkingParameters, m.bsp.registrySettings.wiredNetworkingParameters, "") ' if a device is setup to not use wireless, ensure that wireless is not used if m.stateMachine.modelSupportsWifi then nc = CreateObject("roNetworkConfiguration", 1) if type(nc) = "roNetworkConfiguration" then nc.SetDHCP() nc.SetWiFiESSID("") nc.SetObfuscatedWifiPassphrase("") nc.Apply() endif endif endif endif useWirelessFromRegistry$ = m.bsp.registrySettings.useWireless$ ssidFromRegistry$ = m.bsp.registrySettings.ssid$ passphraseFromRegistry$ = m.bsp.registrySettings.passphrase$ if useWirelessFromRegistry$ <> useWireless$ then m.bsp.WriteRegistrySetting("wifi", useWireless$) m.bsp.registrySettings.useWireless$ = useWireless$ endif if m.stateMachine.useWireless then if ssidFromRegistry$ <> ssid$ then m.bsp.WriteRegistrySetting("ss", ssid$) m.bsp.registrySettings.ssid$ = ssid$ endif if passphraseFromRegistry$ <> passphrase$ then m.bsp.WriteRegistrySetting("pp", passphrase$) m.bsp.registrySettings.passphrase$ = passphrase$ endif endif timeServerFromRegistry$ = m.bsp.registrySettings.timeServer$ if timeServerFromRegistry$ <> timeServer$ then m.bsp.WriteRegistrySetting("ts", timeServer$) m.bsp.registrySettings.timeServer$ = timeServer$ endif ' Retrieve latest net connect spec information from sync spec timeBetweenHeartbeats$ = m.stateMachine.newSync.LookupMetadata("client", "timeBetweenHeartbeats") if timeBetweenHeartbeats$ <> "" then m.stateMachine.timeBetweenHeartbeats% = int(val(timeBetweenHeartbeats$)) endif timeBetweenNetConnects$ = m.stateMachine.newSync.LookupMetadata("client", "timeBetweenNetConnects") if timeBetweenNetConnects$ <> "" then ' check for timeBetweenNetConnects override tbnco$ = m.bsp.registrySettings.tbnco$ if tbnco$ <> "" then timeBetweenNetConnects$ = tbnco$ endif ' if the timeBetweenNetConnects has changed, restart the timer newTimeBetweenNetConnects% = val(timeBetweenNetConnects$) if newTimeBetweenNetConnects% <> m.stateMachine.timeBetweenNetConnects% then m.stateMachine.timeBetweenNetConnects% = newTimeBetweenNetConnects% m.bsp.diagnostics.PrintDebug("### Time between net connects has changed to: " + timeBetweenNetConnects$) else m.bsp.diagnostics.PrintDebug("### Time between net connects = " + timeBetweenNetConnects$) endif endif ' clear any existing timers associated with rate limitings / content download window if type(m.stateMachine.contentDownloadWindowStartTimer) = "roTimer" then m.stateMachine.contentDownloadWindowStartTimer.Stop() endif if type(m.stateMachine.contentDownloadWindowEndTimer) = "roTimer" then m.stateMachine.contentDownloadWindowEndTimer.Stop() endif ' clear any existing timers associated with heartbeats window if type(m.stateMachine.heartbeatsWindowStartTimer) = "roTimer" then m.stateMachine.heartbeatsWindowStartTimer.Stop() endif if type(m.stateMachine.heartbeatsWindowEndTimer) = "roTimer" then m.stateMachine.heartbeatsWindowEndTimer.Stop() endif if m.stateMachine.useWireless then rateLimitModeOutsideWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeOutsideWindow_2") rateLimitRateOutsideWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateOutsideWindow_2") rateLimitModeInWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeInWindow_2") rateLimitRateInWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateInWindow_2") else rateLimitModeOutsideWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeOutsideWindow") rateLimitRateOutsideWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateOutsideWindow") rateLimitModeInWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeInWindow") rateLimitRateInWindowWired$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateInWindow") endif ' needs to also work in the SFN case where the values don't exist in the sync spec (same for contentDownloadsRestricted) ' update rate limiting values SetRateLimitValues(false, m.stateMachine.wiredRateLimits, rateLimitModeOutsideWindowWired$, rateLimitRateOutsideWindowWired$, rateLimitModeInWindowWired$, rateLimitRateInWindowWired$) if m.stateMachine.useWireless then rateLimitModeOutsideWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeOutsideWindow") rateLimitRateOutsideWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateOutsideWindow") rateLimitModeInWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeInWindow") rateLimitRateInWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateInWindow") else rateLimitModeOutsideWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeOutsideWindow_2") rateLimitRateOutsideWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateOutsideWindow_2") rateLimitModeInWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitModeInWindow_2") rateLimitRateInWindowWireless$ = m.stateMachine.newSync.LookupMetadata("client", "rateLimitRateInWindow_2") endif ' needs to also work in the SFN case where the values don't exist in the sync spec (same for contentDownloadsRestricted) ' update rate limiting values SetRateLimitValues(false, m.stateMachine.wirelessRateLimits, rateLimitModeOutsideWindowWireless$, rateLimitRateOutsideWindowWireless$, rateLimitModeInWindowWireless$, rateLimitRateInWindowWireless$) currentTime = m.stateMachine.systemTime.GetLocalDateTime() heartbeatsRestricted = m.stateMachine.newSync.LookupMetadata("client", "heartbeatsRestricted") if heartbeatsRestricted = "yes" then m.stateMachine.heartbeatsRestricted = true heartbeatsRangeStart = m.stateMachine.newSync.LookupMetadata("client", "heartbeatsRangeStart") heartbeatsRangeLength = m.stateMachine.newSync.LookupMetadata("client", "heartbeatsRangeLength") m.stateMachine.heartbeatsRangeStart% = val(heartbeatsRangeStart) m.stateMachine.heartbeatsRangeLength% = val(heartbeatsRangeLength) startOfRange% = m.stateMachine.heartbeatsRangeStart% endOfRange% = startOfRange% + m.stateMachine.heartbeatsRangeLength% notInHeartbeatWindow = OutOfDownloadWindow(currentTime, startOfRange%, endOfRange%) if notInHeartbeatWindow then m.stateMachine.RestartHeartbeatsWindowStartTimer(currentTime, startOfRange%) else m.stateMachine.RestartHeartbeatsWindowEndTimer(currentTime, endOfRange%) endif else m.bsp.diagnostics.PrintDebug("### Heartbeats are unrestricted") m.stateMachine.heartbeatsRestricted = false endif contentDownloadsRestricted = m.stateMachine.newSync.LookupMetadata("client", "contentDownloadsRestricted") if contentDownloadsRestricted = "yes" then m.stateMachine.contentDownloadsRestricted = true contentDownloadRangeStart = m.stateMachine.newSync.LookupMetadata("client", "contentDownloadRangeStart") m.stateMachine.contentDownloadRangeStart% = val(contentDownloadRangeStart) contentDownloadRangeLength = m.stateMachine.newSync.LookupMetadata("client", "contentDownloadRangeLength") m.stateMachine.contentDownloadRangeLength% = val(contentDownloadRangeLength) m.bsp.diagnostics.PrintDebug("### Content downloads are restricted to the time from " + contentDownloadRangeStart + " for " + contentDownloadRangeLength + " minutes.") else m.bsp.diagnostics.PrintDebug("### Content downloads are unrestricted") m.stateMachine.contentDownloadsRestricted = false endif ' Only proceed with sync list download if the current time is within the range of allowed times for content downloads if m.stateMachine.contentDownloadsRestricted then startOfRange% = m.stateMachine.contentDownloadRangeStart% endOfRange% = startOfRange% + m.stateMachine.contentDownloadRangeLength% notInDownloadWindow = OutOfDownloadWindow(currentTime, startOfRange%, endOfRange%) if notInDownloadWindow then m.bsp.diagnostics.PrintDebug("### Not in window to download content") m.stateMachine.AddDeviceDownloadItem("SyncSpecUnchanged", "", "") m.stateMachine.numRetries% = 0 m.stateMachine.currentTimeBetweenNetConnects% = m.stateMachine.timeBetweenNetConnects% ' if necessary, upload the list of current files to the server if m.stateMachine.FileListPendingUpload then m.stateMachine.BuildFileDownloadList(m.stateMachine.currentSync) m.stateMachine.UploadDeviceDownloadProgressFileList() m.stateMachine.FileListPendingUpload = false endif m.stateMachine.newSync = invalid ' set timer to go off when download window starts and program rate limit appropriately m.stateMachine.contentDownloadWindowStartTimer = CreateObject("roTimer") hour% = startOfRange% / 60 minute% = startOfRange% - (hour% * 60) timeoutTime = CopyDateTime(currentTime) timeoutTime.SetHour(hour%) timeoutTime.SetMinute(minute%) timeoutTime.SetSecond(0) timeoutTime.SetMillisecond(0) GetNextTimeout(m.stateMachine.systemTime, timeoutTime) m.stateMachine.contentDownloadWindowStartTimer.SetDateTime(timeoutTime) m.stateMachine.contentDownloadWindowStartTimer.SetPort(m.stateMachine.msgPort) m.stateMachine.contentDownloadWindowStartTimer.Start() m.bsp.diagnostics.PrintDebug("SyncSpecXferEvent: set timer to start of content download window" + timeoutTime.GetString()) SetDownloadRateLimit(m.bsp.diagnostics, 0, m.stateMachine.wiredRateLimits.rlOutsideWindow%) if m.stateMachine.useWireless then SetDownloadRateLimit(m.bsp.diagnostics, 1, m.stateMachine.wirelessRateLimits.rlOutsideWindow%) endif return m.stateMachine.stWaitForTimeout else ' set timer to go off when download window ends and program rate limit appropriately m.stateMachine.contentDownloadWindowEndTimer = CreateObject("roTimer") currentTime.SetHour(0) currentTime.SetMinute(0) currentTime.SetSecond(0) currentTime.SetMillisecond(0) currentTime.AddSeconds(endOfRange% * 60) currentTime.Normalize() GetNextTimeout(m.stateMachine.systemTime, currentTime) m.stateMachine.contentDownloadWindowEndTimer.SetDateTime(currentTime) m.stateMachine.contentDownloadWindowEndTimer.SetPort(m.stateMachine.msgPort) m.stateMachine.contentDownloadWindowEndTimer.Start() m.bsp.diagnostics.PrintDebug("STNetworkSchedulerEventHandler: set timer to end of content download window - " + currentTime.GetString()) SetDownloadRateLimit(m.bsp.diagnostics, 0, m.stateMachine.wiredRateLimits.rlInWindow%) if m.stateMachine.useWireless then SetDownloadRateLimit(m.bsp.diagnostics, 1, m.stateMachine.wirelessRateLimits.rlInWindow%) endif endif else SetDownloadRateLimit(m.bsp.diagnostics, 0, m.stateMachine.wiredRateLimits.rlOutsideWindow%) if m.stateMachine.useWireless then SetDownloadRateLimit(m.bsp.diagnostics, 1, m.stateMachine.wirelessRateLimits.rlOutsideWindow%) endif endif return m.stateMachine.stDownloadingSyncFiles else if event.GetResponseCode() = 404 then m.bsp.diagnostics.PrintDebug("### Server has no sync list for us: 404") m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_NO_SYNCSPEC_AVAILABLE, "404") else ' retry - server returned something other than a 200 or 404 m.stateMachine.ResetDownloadTimerToDoRetry() m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_RETRIEVE_SYNCSPEC_FAILURE, str(event.GetResponseCode())) m.bsp.diagnostics.PrintDebug("### Failed to download sync list: " + str(event.GetResponseCode())) m.stateMachine.AddDeviceErrorItem("deviceError", "", "Failed to download sync list", str(event.GetResponseCode())) endif return m.stateMachine.stWaitForTimeout End Function Sub SetDownloadRateLimit(diagnostics As Object, networkConfigurationIndex% As Integer, rateLimit% As Integer) nc = CreateObject("roNetworkConfiguration", networkConfigurationIndex%) if type(nc) = "roNetworkConfiguration" diagnostics.PrintDebug("SetInboundShaperRate to " + stri(rateLimit%)) ok = nc.SetInboundShaperRate(rateLimit%) if not ok then print "Failure calling SetInboundShaperRate with parameter ";rateLimit% ok = nc.Apply() if not ok then print "Failure calling roNetworkConfiguration.Apply()" endif End Sub Function UpdateRegistrySetting(newValue$ As String, existingValue$ As String, registryKey$ As String) As String if lcase(newValue$) <> lcase(existingValue$) then m.bsp.WriteRegistrySetting(registryKey$, newValue$) endif return newValue$ End Function Sub ConfigureNetwork(networkingParameters As Object, registryNetworkingParameters As Object, registryKeySuffix$ As String) nc = CreateObject("roNetworkConfiguration", networkingParameters.networkConfigurationIndex%) if type(nc) = "roNetworkConfiguration" then if networkingParameters.useDHCP$ = "no" then nc.SetIP4Address(networkingParameters.staticIPAddress$) nc.SetIP4Netmask(networkingParameters.subnetMask$) ' nc.SetIP4Broadcast(networkingParameters.broadcast$) nc.SetIP4Gateway(networkingParameters.gateway$) if networkingParameters.dns1$ <> "" then nc.AddDNSServer(networkingParameters.dns1$) if networkingParameters.dns2$ <> "" then nc.AddDNSServer(networkingParameters.dns2$) if networkingParameters.dns3$ <> "" then nc.AddDNSServer(networkingParameters.dns3$) else nc.SetDHCP() endif nc.SetRoutingMetric(int(val(networkingParameters.networkConnectionPriority$))) if networkingParameters.useWireless then nc.SetWiFiESSID(networkingParameters.ssid$) nc.SetObfuscatedWifiPassphrase(networkingParameters.passphrase$) endif timeServer$ = m.stateMachine.newSync.LookupMetadata("client", "timeServer") nc.SetTimeServer(networkingParameters.timeServer$) success = nc.Apply() nc = invalid if not success then m.bsp.diagnostics.PrintDebug("### roNetworkConfiguration.Apply failure.") else ' save parameters to the registry networkConnectionPriorityFromRegistry$ = registryNetworkingParameters.networkConnectionPriority$ if networkConnectionPriorityFromRegistry$ <> networkingParameters.networkConnectionPriority$ then m.bsp.WriteRegistrySetting("ncp" + registryKeySuffix$, networkingParameters.networkConnectionPriority$) registryNetworkingParameters.networkConnectionPriority$ = networkingParameters.networkConnectionPriority$ endif if networkingParameters.useDHCP$ = "no" then if registryNetworkingParameters.useDHCP$ <> "no" then m.bsp.WriteRegistrySetting("dhcp" + registryKeySuffix$, "no") registryNetworkingParameters.useDHCP$ = "no" endif staticIPAddressFromRegistry$ = registryNetworkingParameters.staticIPAddress$ subnetMaskFromRegistry$ = registryNetworkingParameters.subnetMask$ gatewayFromRegistry$ = registryNetworkingParameters.gateway$ ' broadcastFromRegistry$ = registryNetworkingParameters.broadcast$ dns1FromRegistry$ = registryNetworkingParameters.dns1$ dns2FromRegistry$ = registryNetworkingParameters.dns2$ dns3FromRegistry$ = registryNetworkingParameters.dns3$ if staticIPAddressFromRegistry$ <> networkingParameters.staticIPAddress$ then m.bsp.WriteRegistrySetting("sip" + registryKeySuffix$, networkingParameters.staticIPAddress$) registryNetworkingParameters.staticIPAddress$ = networkingParameters.staticIPAddress$ endif if subnetMaskFromRegistry$ <> networkingParameters.subnetMask$ then m.bsp.WriteRegistrySetting("sm" + registryKeySuffix$, networkingParameters.subnetMask$) registryNetworkingParameters.subnetMask$ = networkingParameters.subnetMask$ endif if gatewayFromRegistry$ <> networkingParameters.gateway$ then m.bsp.WriteRegistrySetting("gw" + registryKeySuffix$, networkingParameters.gateway$) registryNetworkingParameters.gateway$ = networkingParameters.gateway$ endif if dns1FromRegistry$ <> networkingParameters.dns1$ then m.bsp.WriteRegistrySetting("d1" + registryKeySuffix$, networkingParameters.dns1$) registryNetworkingParameters.dns1$ = networkingParameters.dns1$ endif if dns2FromRegistry$ <> networkingParameters.dns2$ then m.bsp.WriteRegistrySetting("d2" + registryKeySuffix$, networkingParameters.dns2$) registryNetworkingParameters.dns2$ = networkingParameters.dns2$ endif if dns3FromRegistry$ <> networkingParameters.dns3$ then m.bsp.WriteRegistrySetting("d3" + registryKeySuffix$, networkingParameters.dns3$) registryNetworkingParameters.dns3$ = networkingParameters.dns3$ endif else if registryNetworkingParameters.useDHCP$ <> "yes" then m.bsp.WriteRegistrySetting("dhcp" + registryKeySuffix$, "yes") registryNetworkingParameters.useDHCP$ = "yes" endif endif endif else m.bsp.diagnostics.PrintDebug("Unable to create roNetworkConfiguration - index = " + stri(networkConfigurationIndex%)) endif End Sub Function STDownloadingSyncFilesEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") nextState = m.StartSyncListDownload() if type(nextState) = "roAssociativeArray" then stop ' can't do this - no transitions on entry '!!!!!!!!!!!!!!!!!! - is this a violation? performing a transition on an entry signal? stateData.nextState = nextState return "TRANSITION" endif return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") else if event["EventType"] = "CANCEL_DOWNLOADS" then m.bsp.diagnostics.PrintDebug("Cancel assetFetcher downloads message received") if type(m.stateMachine.assetFetcher) = "roAssetFetcher" then m.bsp.diagnostics.PrintDebug("Cancel assetFetcher downloads") m.stateMachine.assetFetcher.AsyncCancel() m.stateMachine.assetFetcher = invalid stateData.nextState = m.stateMachine.stWaitForTimeout return "TRANSITION" else return "HANDLED" endif endif endif else if type(event) = "roAssetFetcherProgressEvent" then m.bsp.diagnostics.PrintDebug("### File download progress " + event.GetFileName() + str(event.GetCurrentFilePercentage())) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_PROGRESS, event.GetFileName() + chr(9) + str(event.GetCurrentFilePercentage())) fileIndex% = event.GetFileIndex() fileItem = m.stateMachine.newSync.GetFile("download", fileIndex%) if type(fileItem) = "roAssociativeArray" then m.stateMachine.AddDeviceDownloadProgressItem(fileItem, str(event.GetCurrentFilePercentage()), "ok") endif return "HANDLED" else if type(event) = "roAssetFetcherEvent" then if event.GetUserData() = "BSN" then nextState = m.HandleAssetFetcherEvent(event) if type(nextState) = "roAssociativeArray" then stateData.nextState = nextState return "TRANSITION" endif return "HANDLED" endif endif stateData.nextState = m.superState return "SUPER" End Function Function StartSyncListDownload() As Object m.bsp.diagnostics.PrintDebug("### Start sync list download") m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_DOWNLOAD_START, "") m.stateMachine.AddEventItem("StartSyncListDownload", m.stateMachine.newSync.GetName(), "") m.bsp.assetPool.ReserveMegabytes(50) m.stateMachine.assetFetcher = CreateObject("roAssetFetcher", m.bsp.assetPool) m.stateMachine.assetFetcher.SetUserData("BSN") m.stateMachine.assetFetcher.SetPort(m.stateMachine.msgPort) if m.stateMachine.setUserAndPassword then m.stateMachine.assetFetcher.SetUserAndPassword(m.stateMachine.user$, m.stateMachine.password$) m.stateMachine.assetFetcher.EnableUnsafeAuthentication(m.stateMachine.enableUnsafeAuthentication) m.stateMachine.assetFetcher.SetMinimumTransferRate(1000,900) m.stateMachine.assetFetcher.SetHeaders(m.stateMachine.newSync.GetMetadata("server")) m.stateMachine.assetFetcher.AddHeader("DeviceID", m.stateMachine.deviceUniqueID$) m.stateMachine.assetFetcher.AddHeader("DeviceModel", m.stateMachine.deviceModel$) m.stateMachine.assetFetcher.AddHeader("DeviceFamily", m.stateMachine.deviceFamily$) m.stateMachine.assetFetcher.SetFileProgressIntervalSeconds(15) binding% = GetBinding(m.stateMachine.bsp.contentXfersEnabledWired, m.stateMachine.bsp.contentXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for assetFetcher is " + stri(binding%)) ok = m.stateMachine.assetFetcher.BindToInterface(binding%) if not ok then stop ' clear file download failure count m.stateMachine.fileDownloadFailureCount% = 0 ' this error implies that the current sync list is corrupt - go back to sync list in registry and reboot - no need to retry. do this by deleting autorun.brs and rebooting if (not m.bsp.assetPool.ProtectAssets("BNM-new", m.stateMachine.newSync)) or (not m.bsp.assetPool.ProtectAssets("current", m.stateMachine.currentSync)) then ' don't allow download to delete current files m.stateMachine.LogProtectFilesFailure() endif if m.stateMachine.proxy_mode then m.stateMachine.assetFetcher.AddHeader("Roku-Cache-Request", "Yes") endif if m.stateMachine.syncType$ = "download" then if m.stateMachine.downloadOnlyIfCached then m.stateMachine.assetFetcher.AddHeader("Cache-Control", "only-if-cached") if not m.stateMachine.assetFetcher.AsyncDownload(m.stateMachine.newSync) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_IMMEDIATE_FAILURE, m.stateMachine.assetFetcher.GetFailureReason()) m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### AsyncDownload failed: " + m.stateMachine.assetFetcher.GetFailureReason()) m.stateMachine.AddDeviceErrorItem("deviceError", m.stateMachine.newSync.GetName(), "AsyncDownloadFailure: " + m.stateMachine.assetFetcher.GetFailureReason(), "") m.stateMachine.ResetDownloadTimerToDoRetry() m.stateMachine.newSync = invalid return m.stateMachine.stWaitForTimeout endif else m.stateMachine.assetFetcher.AsyncSuggestCache(m.stateMachine.newSync) endif return 0 End Function Function HandleAssetFetcherEvent(event As Object) As Object m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### assetFetcher_event") if (event.GetEvent() = m.stateMachine.POOL_EVENT_FILE_DOWNLOADED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_COMPLETE, event.GetName()) m.bsp.diagnostics.PrintDebug("### File downloaded " + event.GetName()) ' see if the user should be charged for this download if m.stateMachine.chargeableFiles.DoesExist(event.GetName()) then filePath$ = m.stateMachine.assetPoolFiles.GetPoolFilePath(event.GetName()) file = CreateObject("roReadFile", filePath$) if type(file) = "roReadFile" then file.SeekToEnd() totalContentDownloaded# = m.stateMachine.contentDownloaded# totalContentDownloaded# = totalContentDownloaded# + file.CurrentPosition() m.stateMachine.contentDownloaded# = totalContentDownloaded# m.bsp.diagnostics.PrintDebug("### File size " + str(file.CurrentPosition())) m.bsp.diagnostics.PrintDebug("### Content downloaded = " + str(m.stateMachine.contentDownloaded#)) endif file = invalid endif else if (event.GetEvent() = m.stateMachine.POOL_EVENT_FILE_FAILED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_FILE_DOWNLOAD_FAILURE, event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### File failed " + event.GetName() + ": " + event.GetFailureReason()) m.stateMachine.AddDeviceErrorItem("FileDownloadFailure", event.GetName(), event.GetFailureReason(), str(event.GetResponseCode())) ' log this error to the download progress handler fileIndex% = event.GetFileIndex() fileItem = m.stateMachine.newSync.GetFile("download", fileIndex%) if type(fileItem) = "roAssociativeArray" then m.stateMachine.AddDeviceDownloadProgressItem(fileItem, "-1", event.GetFailureReason()) endif m.stateMachine.fileDownloadFailureCount% = m.stateMachine.fileDownloadFailureCount% + 1 if m.stateMachine.fileDownloadFailureCount% >= m.stateMachine.maxFileDownloadFailures% then m.bsp.diagnostics.PrintDebug("### " + stri(m.stateMachine.maxFileDownloadFailures%) + " file download failures - set timer for retry.") m.stateMachine.assetFetcher.AsyncCancel() m.stateMachine.ResetDownloadTimerToDoRetry() m.stateMachine.assetFetcher = invalid return m.stateMachine.stWaitForTimeout endif else if (event.GetEvent() = m.stateMachine.POOL_EVENT_ALL_FAILED) then if m.stateMachine.syncType$ = "download" then m.stateMachine.ResetDownloadTimerToDoRetry() m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_FAILURE, event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Sync failed: " + event.GetFailureReason()) m.stateMachine.AddDeviceErrorItem("POOL_EVENT_ALL_FAILED", "", event.GetFailureReason(), str(event.GetResponseCode())) ' capture total content downloaded m.bsp.diagnostics.PrintDebug("### Total content downloaded = " + str(m.stateMachine.contentDownloaded#)) ok = m.stateMachine.UploadTrafficDownload(m.stateMachine.contentDownloaded#) if ok then m.stateMachine.contentDownloaded# = 0 endif else m.bsp.diagnostics.PrintDebug("### Proxy mode sync complete") endif m.stateMachine.newSync = invalid m.stateMachine.assetFetcher = invalid return m.stateMachine.stWaitForTimeout else if (event.GetEvent() = m.stateMachine.POOL_EVENT_ALL_DOWNLOADED) then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_DOWNLOAD_COMPLETE, "") m.bsp.diagnostics.PrintDebug("### All files downloaded") m.stateMachine.AddDeviceDownloadItem("All files downloaded", "", "") ' send up the list of files downloaded m.stateMachine.BuildFileDownloadList(m.stateMachine.newSync) m.stateMachine.UploadDeviceDownloadProgressFileList() m.stateMachine.FileListPendingUpload = false ' capture total content downloaded m.bsp.diagnostics.PrintDebug("### Total content downloaded = " + str(m.stateMachine.contentDownloaded#)) ok = m.stateMachine.UploadTrafficDownload(m.stateMachine.contentDownloaded#) if ok then m.stateMachine.contentDownloaded# = 0 endif ' Log the end of sync list download m.stateMachine.AddEventItem("EndSyncListDownload", m.stateMachine.newSync.GetName(), str(event.GetResponseCode())) ' Clear retry count and reset timeout period m.stateMachine.numRetries% = 0 m.stateMachine.currentTimeBetweenNetConnects% = m.stateMachine.timeBetweenNetConnects% ' diagnostic web server dwsParams = GetDWSParams(m.stateMachine.newSync, m.bsp.registrySettings) dwsRebootRequired = false nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" dwsAA = CreateObject("roAssociativeArray") if dwsParams.dwsEnabled$ = "yes" then dwsAA["port"] = "80" dwsAA["password"] = dwsParams.dwsPassword$ endif dwsRebootRequired = nc.SetupDWS(dwsAA) nc = invalid endif oldSyncSpecScriptsOnly = m.stateMachine.currentSync.FilterFiles("download", { group: "script" } ) newSyncSpecScriptsOnly = m.stateMachine.newSync.FilterFiles("download", { group: "script" } ) rebootRequired = false if not oldSyncSpecScriptsOnly.FilesEqualTo(newSyncSpecScriptsOnly) then realizer = CreateObject("roAssetRealizer", m.bsp.assetPool, "/") globalAA = GetGlobalAA() globalAA.bsp.msgPort.DeferWatchdog(120) event = realizer.Realize(newSyncSpecScriptsOnly) realizer = invalid if event.GetEvent() <> m.stateMachine.EVENT_REALIZE_SUCCESS then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_REALIZE_FAILURE, stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Realize failed " + stri(event.GetEvent()) + chr(9) + event.GetName() + chr(9) + event.GetFailureReason() ) m.stateMachine.AddDeviceErrorItem("RealizeFailure", event.GetName(), event.GetFailureReason(), str(event.GetEvent())) m.stateMachine.newSync = invalid m.stateMachine.assetFetcher = invalid return m.stateMachine.stWaitForTimeout endif ' reboot if successful rebootRequired = true endif ' Save to current-sync.xml then do cleanup if not m.stateMachine.newSync.WriteToFile("current-sync.xml") then stop timezone = m.stateMachine.newSync.LookupMetadata("client", "timezone") if timezone <> "" then m.stateMachine.systemTime.SetTimeZone(timezone) endif m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### DOWNLOAD COMPLETE") DeleteFile("bad-sync.xml") if rebootRequired then m.bsp.diagnostics.PrintDebug("### new script or upgrade found - reboot") m.stateMachine.AddEventItem("DownloadComplete - new script or upgrade file found", m.stateMachine.newSync.GetName(), "") m.stateMachine.RebootAfterEventsSent() endif if dwsRebootRequired then m.bsp.diagnostics.PrintDebug("### DWS parameter change - reboot") m.stateMachine.AddEventItem("DownloadComplete - DWS parameter change", m.stateMachine.newSync.GetName(), "") m.stateMachine.RebootAfterEventsSent() endif m.stateMachine.assetCollection = m.stateMachine.newSync.GetAssets("download") m.stateMachine.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.assetPool, m.stateMachine.assetCollection) if type(m.stateMachine.assetPoolFiles) <> "roAssetPoolFiles" then stop globalAA = GetGlobalAA() globalAA.autoscheduleFilePath$ = GetPoolFilePath(m.stateMachine.assetPoolFiles, "autoschedule.xml") globalAA.resourcesFilePath$ = GetPoolFilePath(m.stateMachine.assetPoolFiles, "resources.txt") globalAA.boseProductsFilePath$ = GetPoolFilePath(m.stateMachine.assetPoolFiles, "BoseProducts.xml") if globalAA.autoscheduleFilePath$ = "" then stop m.stateMachine.newSync = invalid m.stateMachine.assetFetcher = invalid ' reset m.currentSync (could the script just do m.currentSync = m.newSync earlier?) m.stateMachine.currentSync = CreateObject("roSyncSpec") if type(m.stateMachine.currentSync) <> "roSyncSpec" then stop if not m.stateMachine.currentSync.ReadFromFile("current-sync.xml") then stop m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### return from HandleAssetFetcherEvent") debugOn = false if m.stateMachine.currentSync.LookupMetadata("client", "enableSerialDebugging") = "True" then debugOn = true endif m.bsp.diagnostics.UpdateDebugOn(debugOn) systemLogDebugOn = false if m.stateMachine.currentSync.LookupMetadata("client", "enableSystemLogDebugging") = "True" then systemLogDebugOn = true endif m.bsp.diagnostics.UpdateSystemLogDebugOn(systemLogDebugOn) ' send internal message to prepare for restart prepareForRestartEvent = CreateObject("roAssociativeArray") prepareForRestartEvent["EventType"] = "PREPARE_FOR_RESTART" m.stateMachine.msgPort.PostMessage(prepareForRestartEvent) ' send internal message indicating that new content is available contentUpdatedEvent = CreateObject("roAssociativeArray") contentUpdatedEvent["EventType"] = "CONTENT_UPDATED" m.stateMachine.msgPort.PostMessage(contentUpdatedEvent) return m.stateMachine.stWaitForTimeout endif End Function Sub LogProtectFilesFailure() m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.assetPool.GetFailureReason()) m.stateMachine.logging.FlushLogFile() DeleteFile("autorun.brs") m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.assetPool.GetFailureReason()) m.stateMachine.AddDeviceErrorItem("deviceError", m.stateMachine.currentSync.GetName(), "ProtectFilesFailure: " + m.bsp.assetPool.GetFailureReason(), "") globalAA = GetGlobalAA() globalAA.bsp.msgPort.DeferWatchdog(15) msg = wait(10000, 0) ' wait for either a timeout (10 seconds) or a message indicating that the post was complete a=RebootSystem() End Sub 'endregion 'region BP State Machine ' ************************************************* ' ' BP State Machine ' ' ************************************************* Function newBPStateMachine(bsp As Object, inputPortIdentity$ As String, buttonPanelIndex% As Integer, buttonNumber% As Integer) As Object BPStateMachine = CreateObject("roAssociativeArray") BPStateMachine.bsp = bsp BPStateMachine.msgPort = bsp.msgPort BPStateMachine.inputPortIdentity$ = inputPortIdentity$ BPStateMachine.buttonPanelIndex% = buttonPanelIndex% BPStateMachine.buttonNumber% = buttonNumber% BPStateMachine.timer = invalid BPStateMachine.configuration$ = "press" BPStateMachine.initialHoldoff% = -1 BPStateMachine.repeatInterval% = -1 BPStateMachine.ConfigureButton = ConfigureButton BPStateMachine.EventHandler = BPEventHandler BPStateMachine.state$ = "ButtonUp" return BPStateMachine End Function Sub BPEventHandler(event As Object) if m.state$ = "ButtonUp" then if type(event) = "roControlDown" and stri(event.GetSourceIdentity()) = m.inputPortIdentity$ and m.buttonNumber% = event.GetInt() then m.bsp.diagnostics.PrintDebug("BP control down" + str(event.GetInt())) bpControlDown = CreateObject("roAssociativeArray") bpControlDown["EventType"] = "BPControlDown" bpControlDown["ButtonPanelIndex"] = StripLeadingSpaces(str(m.buttonPanelIndex%)) bpControlDown["ButtonNumber"] = StripLeadingSpaces(str(event.GetInt())) m.msgPort.PostMessage(bpControlDown) if m.configuration$ = "pressContinuous" then m.timer = CreateObject("roTimer") m.timer.SetPort(m.msgPort) m.timer.SetElapsed(0, m.initialHoldoff%) m.timer.Start() endif m.state$ = "ButtonDown" endif else if type(event) = "roControlUp" and stri(event.GetSourceIdentity()) = m.inputPortIdentity$ and m.buttonNumber% = event.GetInt() then m.bsp.diagnostics.PrintDebug("BP control up" + str(event.GetInt())) ' if continuous, stop and destroy the timer if type(m.timer) = "roTimer" then m.timer.Stop() m.timer = invalid endif m.state$ = "ButtonUp" ' else check for repeat timeout else if type(event) = "roTimerEvent" and type(m.timer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.timer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("BP REPEAT control down" + str(m.buttonNumber%)) bpControlDown = CreateObject("roAssociativeArray") bpControlDown["EventType"] = "BPControlDown" bpControlDown["ButtonPanelIndex"] = StripLeadingSpaces(str(m.buttonPanelIndex%)) bpControlDown["ButtonNumber"] = StripLeadingSpaces(str(m.buttonNumber%)) m.msgPort.PostMessage(bpControlDown) m.timer.SetElapsed(0, m.repeatInterval%) m.timer.Start() endif endif endif End Sub Sub ConfigureButton(bpSpec As Object) bpConfiguration$ = bpSpec.configuration$ ' no change necessary if the old and new configurations are the same and are a simple press if bpConfiguration$ = "press" and m.configuration$ = "press" then return ' if the old configuration was continuous and the new configuration is a simple press, stop the timer and destroy the timer object if bpConfiguration$ = "press" and m.configuration$ = "pressContinuous" then if type(m.timer) = "roTimer" then m.timer.Stop() m.timer = invalid endif m.configuration$ = "press" return endif ' capture the repeat rates if the new configuration is continuous if bpConfiguration$ = "pressContinuous" then m.initialHoldoff% = int(val(bpSpec.initialHoldoff$)) m.repeatInterval% = int(val(bpSpec.repeatInterval$)) endif ' if both the old and new configurations are continuous, restart the timer (if it is active) if bpConfiguration$ = "pressContinuous" and m.configuration$ = "pressContinuous" then if type(m.timer) = "roTimer" then m.timer.Stop() m.timer.SetElapsed(0, m.initialHoldoff%) m.timer.Start() endif endif ' if the old configuration was simple press and the new configuration is continuous, then capture the new values ' but don't start a timer (repeating won't start if the button was down at the time the state is entered). m.configuration$ = bpConfiguration$ End Sub 'endregion 'region GPIO State Machine ' ************************************************* ' ' GPIO State Machine ' ' ************************************************* Function newGPIOStateMachine(bsp As Object, controlPort As Object, inputPortIdentity$ As String, buttonNumber% As Integer) As Object GPIOStateMachine = CreateObject("roAssociativeArray") GPIOStateMachine.bsp = bsp GPIOStateMachine.msgPort = bsp.msgPort GPIOStateMachine.inputPortIdentity$ = inputPortIdentity$ GPIOStateMachine.buttonNumber% = buttonNumber% GPIOStateMachine.timer = invalid GPIOStateMachine.configuration$ = "press" GPIOStateMachine.initialHoldoff% = -1 GPIOStateMachine.repeatInterval% = -1 GPIOStateMachine.ConfigureButton = ConfigureButton GPIOStateMachine.EventHandler = GPIOEventHandler if controlPort.IsInputActive(buttonNumber%) then GPIOStateMachine.state$ = "ButtonDown" else GPIOStateMachine.state$ = "ButtonUp" endif return GPIOStateMachine End Function Sub GPIOEventHandler(event As Object) if m.state$ = "ButtonUp" then if type(event) = "roControlDown" and stri(event.GetSourceIdentity()) = m.inputPortIdentity$ and m.buttonNumber% = event.GetInt() then m.bsp.diagnostics.PrintDebug("GPIO control down" + str(event.GetInt())) gpioControlDown = CreateObject("roAssociativeArray") gpioControlDown["EventType"] = "GPIOControlDown" gpioControlDown["ButtonNumber"] = StripLeadingSpaces(str(event.GetInt())) m.msgPort.PostMessage(gpioControlDown) if m.configuration$ = "pressContinuous" then m.timer = CreateObject("roTimer") m.timer.SetPort(m.msgPort) m.timer.SetElapsed(0, m.initialHoldoff%) m.timer.Start() endif m.state$ = "ButtonDown" endif else if type(event) = "roControlUp" and stri(event.GetSourceIdentity()) = m.inputPortIdentity$ and m.buttonNumber% = event.GetInt() then m.bsp.diagnostics.PrintDebug("GPIO control up" + str(event.GetInt())) ' if continuous, stop and destroy the timer if type(m.timer) = "roTimer" then m.timer.Stop() m.timer = invalid endif gpioControlUp = CreateObject("roAssociativeArray") gpioControlUp["EventType"] = "GPIOControlUp" gpioControlUp["ButtonNumber"] = StripLeadingSpaces(str(event.GetInt())) m.msgPort.PostMessage(gpioControlUp) m.state$ = "ButtonUp" ' else check for repeat timeout else if type(event) = "roTimerEvent" and type(m.timer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.timer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("GPIO REPEAT control down" + str(m.buttonNumber%)) gpioControlDown = CreateObject("roAssociativeArray") gpioControlDown["EventType"] = "GPIOControlDown" gpioControlDown["ButtonNumber"] = StripLeadingSpaces(str(m.buttonNumber%)) m.msgPort.PostMessage(gpioControlDown) m.timer.SetElapsed(0, m.repeatInterval%) m.timer.Start() endif endif endif End Sub 'endregion 'region EventLoop ' ************************************************* ' ' Event Loop and associated processing ' ' ************************************************* Sub EventLoop() SQLITE_COMPLETE = 100 while true msg = wait(0, m.msgPort) m.diagnostics.PrintTimestamp() m.diagnostics.PrintDebug("msg received - type=" + type(msg)) 'custom clean 6-17-15 lca m.sdcl.processevent(msg) 'look for custom clean timer if m.defwatchdog = false m.msgport.DeferWatchdog(900) '15 minutes m.defwatchdog = true endif if type(msg) = "roControlDown" and stri(msg.GetSourceIdentity()) = stri(m.controlPort.GetIdentity()) then if msg.GetInt()=12 then stop endif endif eventHandled = false for each scriptPlugin in m.scriptPlugins ' ERR_NORMAL_END = &hFC if scriptPlugin.plugin = invalid then m.diagnostics.PrintDebug("Plugin for " + scriptPlugin.name$ + " is invalid.") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, scriptPlugin.name$) else eventHandled = scriptPlugin.plugin.ProcessEvent(msg) if eventHandled then exit for endif endif ' retVal = Eval("eventHandled = scriptPlugin.plugin.ProcessEvent(msg)") ' if retVal <> &hFC then ' ' log the failure ' m.diagnostics.PrintDebug("Failure executing Eval to execute script plugin file event handler: return value = " + stri(retVal)) ' m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, stri(retVal) + chr(9) + "EventHandler") ' endif next ' don't propagate the event if it was handled by a plugin if not eventHandled then if type(msg) = "roSqliteEvent" then if msg.GetSqlResult() <> SQLITE_COMPLETE then m.diagnostics.PrintDebug("roSqliteEvent.GetSqlResult() <> SQLITE_COMPLETE") if type(msg.GetSqlResult()) = "roInt" then m.diagnostics.PrintDebug("roSqliteEvent.GetSqlResult() = " + stri(roSqliteEvent.GetSqlResult())) endif endif endif if type(msg) = "roHttpEvent" then userdata = msg.GetUserData() if type(userdata) = "roAssociativeArray" and type(userdata.HandleEvent) = "roFunction" then userData.HandleEvent(userData, msg) endif else m.playerHSM.Dispatch(msg) for buttonPanelIndex% = 0 to 2 for i% = 0 to 10 if type(m.bpSM[buttonPanelIndex%, i%]) = "roAssociativeArray" then m.bpSM[buttonPanelIndex%, i%].EventHandler(msg) endif next next for i% = 0 to 7 if type(m.gpioSM[i%]) = "roAssociativeArray" then m.gpioSM[i%].EventHandler(msg) endif next if type(m.sign) = "roAssociativeArray" then numZones% = m.sign.zonesHSM.Count() for i% = 0 to numZones% - 1 m.dispatchingZone = m.sign.zonesHSM[i%] m.dispatchingZone.Dispatch(msg) next endif if m.networkingActive then m.networkingHSM.Dispatch(msg) endif endif endif end while End Sub Function ExecuteSwitchPresentationCommand(presentationName$ As String) As Boolean ' retrieve target presentation presentation = m.presentations.Lookup(presentationName$) if type(presentation) = "roAssociativeArray" then ' check for existence of target presentation - if it's not present, don't try to switch to it autoplayFileName$ = "autoplay-" + presentation.presentationName$ + ".xml" xmlFileName$ = m.assetPoolFiles.GetPoolFilePath(autoplayFileName$) if xmlFileName$ = "" then m.diagnostics.PrintDebug("switchPresentation: target presentation not found - " + presentationName$) return false endif ' send internal message to prepare for restart prepareForRestartEvent = CreateObject("roAssociativeArray") prepareForRestartEvent["EventType"] = "PREPARE_FOR_RESTART" m.msgPort.PostMessage(prepareForRestartEvent) ' send switch presentation internal message switchPresentationEvent = CreateObject("roAssociativeArray") switchPresentationEvent["EventType"] = "SWITCH_PRESENTATION" switchPresentationEvent["Presentation"] = presentation.presentationName$ m.msgPort.PostMessage(switchPresentationEvent) return true endif return false End Function Sub ExecuteMediaStateCommands(zoneHSM As Object, cmds As Object) if type(cmds) = "roArray" then for each cmd in cmds if cmd.name$ = "switchPresentation" then m.diagnostics.PrintDebug("switchPresentation: not supported by media state commands") return endif m.ExecuteCmd(zoneHSM, cmd.name$, cmd.parameters) next endif End Sub Function ExecuteTransitionCommands(zoneHSM As Object, transition As Object) As Boolean transitionCmds = transition.transitionCmds if type(transitionCmds) = "roArray" then for each transitionCmd in transitionCmds command$ = transitionCmd.name$ if command$ = "synchronize" then ' if the next command is synchronize, get the file to preload nextState$ = transition.targetMediaState$ zoneHSM.preloadState = zoneHSM.stateTable[nextState$] else if command$ = "switchPresentation" then presentationName$ = transitionCmd.parameters["presentationName"].GetCurrentParameterValue() return m.ExecuteSwitchPresentationCommand(presentationName$) else if command$ = "internalSynchronize" then if type(transition.internalSynchronizeEventsMaster) = "roAssociativeArray" then activeState = zoneHSM.activeState if type(activeState) = "roAssociativeArray" then activeState.internalSynchronizeEventsMaster = transition.internalSynchronizeEventsMaster endif endif endif m.ExecuteCmd(zoneHSM, transitionCmd.name$, transitionCmd.parameters) next endif return false End Function Function GetNonPrintableKeyboardCode(keyboardInput% As Integer) As String keyboardInput$ = LCase(StripLeadingSpaces(stri(keyboardInput%))) if m.nonPrintableKeyboardKeys.DoesExist(keyboardInput$) then return m.nonPrintableKeyboardKeys.Lookup(keyboardInput$) endif return "" End Function Sub InitializeNonPrintableKeyboardCodeList() ' Space 32 ' Left arrow 32848 ' Right arrow 32847 ' Up arrow 32850 ' Down arrow 32849 ' Return 10 ' Enter 13 ' Escape 27 ' Page Up 32843 ' Page Down 32846 ' F1 32826 ' F2 32827 ' F3 32828 ' F4 32829 ' F5 32830 ' F6 32831 ' F7 32832 ' F8 32833 ' F9 32834 ' F10 32835 ' F11 32836 ' F12 32837 ' F13 (Print Screen) 32838 ' F14 (Scroll Lock) 32839 ' F15 (Pause Break) 32840 ' Backspace 8 ' Tab 9 ' Insert 32841 ' Delete 127 ' Home 32842 ' End 32845 ' Capslock 32825 ' Mute 32895 ' Volume down 32897 ' Volume up 32896 ' Next track 786613 ' Previous track 786614 ' Play/Pause 786637 ' Stop music 786615 ' Stop browsing 786982 ' Power 65665 ' Back 786980 ' Forward 786981 ' Refresh 786983 m.nonPrintableKeyboardKeys = CreateObject("roAssociativeArray") m.nonPrintableKeyboardKeys.AddReplace("8","") m.nonPrintableKeyboardKeys.AddReplace("9","") m.nonPrintableKeyboardKeys.AddReplace("10","") m.nonPrintableKeyboardKeys.AddReplace("13","") m.nonPrintableKeyboardKeys.AddReplace("27","") m.nonPrintableKeyboardKeys.AddReplace("32","") m.nonPrintableKeyboardKeys.AddReplace("127","") m.nonPrintableKeyboardKeys.AddReplace("32848","") m.nonPrintableKeyboardKeys.AddReplace("32847","") m.nonPrintableKeyboardKeys.AddReplace("32850","") m.nonPrintableKeyboardKeys.AddReplace("32849","") m.nonPrintableKeyboardKeys.AddReplace("32843","") m.nonPrintableKeyboardKeys.AddReplace("32846","") m.nonPrintableKeyboardKeys.AddReplace("32826","") m.nonPrintableKeyboardKeys.AddReplace("32827","") m.nonPrintableKeyboardKeys.AddReplace("32828","") m.nonPrintableKeyboardKeys.AddReplace("32829","") m.nonPrintableKeyboardKeys.AddReplace("32830","") m.nonPrintableKeyboardKeys.AddReplace("32831","") m.nonPrintableKeyboardKeys.AddReplace("32832","") m.nonPrintableKeyboardKeys.AddReplace("32833","") m.nonPrintableKeyboardKeys.AddReplace("32834","") m.nonPrintableKeyboardKeys.AddReplace("32835","") m.nonPrintableKeyboardKeys.AddReplace("32836","") m.nonPrintableKeyboardKeys.AddReplace("32837","") m.nonPrintableKeyboardKeys.AddReplace("32838","") m.nonPrintableKeyboardKeys.AddReplace("32839","") m.nonPrintableKeyboardKeys.AddReplace("32840","") m.nonPrintableKeyboardKeys.AddReplace("32841","") m.nonPrintableKeyboardKeys.AddReplace("32842","") m.nonPrintableKeyboardKeys.AddReplace("32845","") m.nonPrintableKeyboardKeys.AddReplace("32825","") m.nonPrintableKeyboardKeys.AddReplace("32895","") m.nonPrintableKeyboardKeys.AddReplace("32897","") m.nonPrintableKeyboardKeys.AddReplace("32896","") m.nonPrintableKeyboardKeys.AddReplace("786613","") m.nonPrintableKeyboardKeys.AddReplace("786614","") m.nonPrintableKeyboardKeys.AddReplace("786637","") m.nonPrintableKeyboardKeys.AddReplace("786615","") m.nonPrintableKeyboardKeys.AddReplace("786982","") m.nonPrintableKeyboardKeys.AddReplace("65665","") m.nonPrintableKeyboardKeys.AddReplace("786980","") m.nonPrintableKeyboardKeys.AddReplace("786981","") m.nonPrintableKeyboardKeys.AddReplace("786983","") End Sub Function ConvertToRemoteCommand(remoteCommand% As Integer) As String Dim remoteCommands[19] remoteCommands[0]="WEST" remoteCommands[1]="EAST" remoteCommands[2]="NORTH" remoteCommands[3]="SOUTH" remoteCommands[4]="SEL" remoteCommands[5]="EXIT" remoteCommands[6]="PWR" remoteCommands[7]="MENU" remoteCommands[8]="SEARCH" remoteCommands[9]="PLAY" remoteCommands[10]="FF" remoteCommands[11]="RW" remoteCommands[12]="PAUSE" remoteCommands[13]="ADD" remoteCommands[14]="SHUFFLE" remoteCommands[15]="REPEAT" remoteCommands[16]="VOLUP" remoteCommands[17]="VOLDWN" remoteCommands[18]="BRIGHT" if remoteCommand% < 0 or remoteCommand% > 18 return "" return remoteCommands[remoteCommand%] End Function Function GetIntegerParameterValue(parameters As Object, parameterName$ as String, defaultValue% As Integer) As Integer parameter = parameters[parameterName$] parameter$ = parameter.GetCurrentParameterValue() parameter% = defaultValue% if parameter$ <> "" then parameter% = int(val(parameter$)) endif return parameter% End Function ' m is bsp Sub ExecuteCmd(zoneHSM As Object, command$ As String, parameters As Object) m.diagnostics.PrintDebug("ExecuteCmd " + command$) if command$ = "gpioOnCommand" then gpioNumberParameter = parameters["gpioNumber"] gpioNumber$ = gpioNumberParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Turn on gpioNumber " + gpioNumber$) m.controlPort.SetOutputState(int(val(gpioNumber$)), 1) else if command$ = "gpioOffCommand" then gpioNumberParameter = parameters["gpioNumber"] gpioNumber$ = gpioNumberParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Turn off gpioNumber " + gpioNumber$) m.controlPort.SetOutputState(int(val(gpioNumber$)), 0) else if command$ = "gpioSetStateCommand" then gpioStateParameter = parameters["stateValue"] gpioState$ = gpioStateParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set GPIO's to " + gpioState$) m.controlPort.SetWholeState(int(val(gpioState$))) else if command$ = "mapDigitalOutputVideo" then if zoneHSM.SendCommandToVideo() then zone = m.GetVideoZone(zoneHSM) if type(zone) = "roAssociativeArray" then m.MapDigitalOutput(zone.videoPlayer, parameters) endif endif else if command$ = "mapDigitalOutputAudio" then m.MapDigitalOutput(zoneHSM.audioPlayer, parameters) else if command$ = "setAllAudioOutputs" then m.SetAllAudioOutputs(parameters) else if command$ = "setAudioMode" then m.SetAudioMode1(parameters) else if command$ = "setAudioOutputVideo" then if zoneHSM.SendCommandToVideo() then m.SetAudioOutput(zoneHSM, true, parameters) endif else if command$ = "setAudioOutputAudio" then m.SetAudioOutput(zoneHSM, false, parameters) else if command$ = "setAudioModeVideo" then if zoneHSM.SendCommandToVideo() then zone = m.GetVideoZone(zoneHSM) if type(zone) = "roAssociativeArray" then m.SetAudioMode(zone.videoPlayer, parameters) endif endif else if command$ = "setAudioModeAudio" then m.SetAudioMode(zoneHSM.audioPlayer, parameters) else if command$ = "mapStereoOutputVideo" then if zoneHSM.SendCommandToVideo() then m.MapStereoOutput(zoneHSM, true, parameters) endif else if command$ = "mapStereoOutputAudio" then m.MapStereoOutput(zoneHSM, false, parameters) else if command$ = "muteAudioOutputs" then m.MuteAudioOutputs(true, parameters) else if command$ = "unmuteAudioOutputs" then m.MuteAudioOutputs(false, parameters) else if command$ = "setConnectorVolume" then m.SetConnectorVolume(parameters) else if command$ = "incrementConnectorVolume" then m.ChangeConnectorVolume(1, parameters) else if command$ = "decrementConnectorVolume" then m.ChangeConnectorVolume(-1, parameters) else if command$ = "setZoneVolume" then m.SetZoneVolume(parameters) else if command$ = "incrementZoneVolume" then m.ChangeZoneVolume(1, parameters) else if command$ = "decrementZoneVolume" then m.ChangeZoneVolume(-1, parameters) else if command$ = "setZoneChannelVolume" then m.SetZoneChannelVolume(parameters) else if command$ = "incrementZoneChannelVolume" then m.ChangeZoneChannelVolume(1, parameters) else if command$ = "decrementZoneChannelVolume" then m.ChangeZoneChannelVolume(-1, parameters) else if command$ = "setSpdifMuteVideo" then if zoneHSM.SendCommandToVideo() then zone = m.GetVideoZone(zoneHSM) if type(zone) = "roAssociativeArray" then m.SetSpdifMute(zone.videoPlayer, parameters) endif endif else if command$ = "setSpdifMuteAudio" then m.SetSpdifMute(zoneHSM.audioPlayer, parameters) else if command$ = "setAnalogMuteVideo" then if zoneHSM.SendCommandToVideo() then zone = m.GetVideoZone(zoneHSM) if type(zone) = "roAssociativeArray" then m.SetAnalogMute(zone.videoChannelVolumes, zone.videoPlayer, parameters) endif endif else if command$ = "setAnalogMuteAudio" then m.SetAnalogMute(zoneHSM.audioChannelVolumes, zoneHSM.audioPlayer, parameters) else if command$ = "setHDMIMute" then m.SetHDMIMute(parameters) else if command$ = "setVideoVolumeByConnector" then if zoneHSM.SendCommandToVideo() then outputParameter = parameters["output"] volumeParameter = parameters["volume"] output$ = outputParameter.GetCurrentParameterValue() volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set video volume on output " + output$ + " to " + volume$) m.SetVideoVolumeByConnector(zoneHSM, output$, volume$) endif else if command$ = "incrementVideoVolumeByConnector" then if zoneHSM.SendCommandToVideo() then outputParameter = parameters["output"] volumeDeltaParameter = parameters["volumeDelta"] output$ = outputParameter.GetCurrentParameterValue() volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment video volume on output " + output$ + " by " + volumeDelta$) m.IncrementVideoVolumeByConnector(zoneHSM, output$, volumeDelta$) endif else if command$ = "decrementVideoVolumeByConnector" then if zoneHSM.SendCommandToVideo() then outputParameter = parameters["output"] volumeDeltaParameter = parameters["volumeDelta"] output$ = outputParameter.GetCurrentParameterValue() volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement video volume on output " + output$ + " by " + volumeDelta$) m.DecrementVideoVolumeByConnector(zoneHSM, output$, volumeDelta$) endif else if command$ = "setVideoVolume" then if zoneHSM.SendCommandToVideo() then volumeParameter = parameters["volume"] volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set video volume to " + volume$) m.SetVideoVolume(zoneHSM, volume$) endif else if command$ = "incrementVideoVolume" then if zoneHSM.SendCommandToVideo() then volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment video volume by " + volumeDelta$) m.IncrementVideoVolume(zoneHSM, volumeDelta$) endif else if command$ = "decrementVideoVolume" then if zoneHSM.SendCommandToVideo() then volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement video volume by " + volumeDelta$) m.DecrementVideoVolume(zoneHSM, volumeDelta$) endif else if command$ = "setAudioVolumeByConnector" then outputParameter = parameters["output"] volumeParameter = parameters["volume"] output$ = outputParameter.GetCurrentParameterValue() volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set audio volume on analog " + output$ + " to " + volume$) m.SetAudioVolumeByConnector(zoneHSM, output$, volume$) else if command$ = "incrementAudioVolumeByConnector" then outputParameter = parameters["output"] output$ = outputParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment audio volume on output " + output$ + " by " + volumeDelta$) m.IncrementAudioVolumeByConnector(zoneHSM, output$, volumeDelta$) else if command$ = "decrementAudioVolumeByConnector" then outputParameter = parameters["output"] output$ = outputParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement audio volume on output " + output$ + " by " + volumeDelta$) m.DecrementAudioVolumeByConnector(zoneHSM, output$, volumeDelta$) else if command$ = "setAudioVolume" then volumeParameter = parameters["volume"] volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set audio volume to " + volume$) m.SetAudioVolume(zoneHSM, volume$) else if command$ = "incrementAudioVolume" then if IsAudioPlayer(zoneHSM.audioPlayer) then volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment audio volume by " + volumeDelta$) m.IncrementAudioVolume(zoneHSM, volumeDelta$, zoneHSM.audioPlayerAudioSettings.maxVolume%) endif else if command$ = "decrementAudioVolume" then if IsAudioPlayer(zoneHSM.audioPlayer) then volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement audio volume by " + volumeDelta$) m.DecrementAudioVolume(zoneHSM, volumeDelta$, zoneHSM.audioPlayerAudioSettings.minVolume%) endif else if command$ = "setVideoChannelVolumes" then if zoneHSM.SendCommandToVideo() then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeParameter = parameters["volume"] volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set video channel volume: channel = " + channelMask$ + ", volume = " + volume$) m.SetVideoChannnelVolume(zoneHSM, channelMask$, volume$) endif else if command$ = "incrementVideoChannelVolumes" then if zoneHSM.SendCommandToVideo() then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment video channel volumes: channel = " + channelMask$ + ", volume delta = " + volumeDelta$) m.IncrementVideoChannnelVolumes(zoneHSM, channelMask$, volumeDelta$) endif else if command$ = "decrementVideoChannelVolumes" then if zoneHSM.SendCommandToVideo() then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement video channel volumes: channel = " + channelMask$ + ", volume delta = " + volumeDelta$) m.DecrementVideoChannnelVolumes(zoneHSM, channelMask$, volumeDelta$) endif else if command$ = "setAudioChannelVolumes" then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeParameter = parameters["volume"] volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set audio channel volume: channel = " + channelMask$ + ", volume = " + volume$) m.SetAudioChannnelVolume(zoneHSM, channelMask$, volume$) else if command$ = "incrementAudioChannelVolumes" then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Increment audio channel volumes: channel = " + channelMask$ + ", volume delta = " + volumeDelta$) m.IncrementAudioChannelVolumes(zoneHSM, channelMask$, volumeDelta$) else if command$ = "decrementAudioChannelVolumes" then channelMaskParameter = parameters["channel"] channelMask$ = channelMaskParameter.GetCurrentParameterValue() volumeDeltaParameter = parameters["volumeDelta"] volumeDelta$ = volumeDeltaParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Decrement audio channel volumes: channel = " + channelMask$ + ", volume delta = " + volumeDelta$) m.DecrementAudioChannelVolumes(zoneHSM, channelMask$, volumeDelta$) else if command$ = "sendSerialStringCommand" then portParameter = parameters["port"] port$ = portParameter.GetCurrentParameterValue() serialStringParameter = parameters["serialString"] serialString$ = serialStringParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendSerialStringCommand " + serialString$ + " to port " + port$) if type(m.serial) = "roAssociativeArray" then serial = m.serial[port$] if type(serial) = "roSerialPort" then serial.SendLine(serialString$) endif endif else if command$ = "sendSerialBlockCommand" then portParameter = parameters["port"] port$ = portParameter.GetCurrentParameterValue() serialStringParameter = parameters["serialString"] serialString$ = serialStringParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendSerialBlockCommand " + serialString$ + " to port " + port$) if type(m.serial) = "roAssociativeArray" then serial = m.serial[port$] if type(serial) = "roSerialPort" then serial.SendBlock(serialString$) endif endif else if command$ = "sendSerialByteCommand" then portParameter = parameters["port"] port$ = portParameter.GetCurrentParameterValue() byteValueParameter = parameters["byteValue"] byteValue$ = byteValueParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendSerialByteCommand " + byteValue$ + " to port " + port$) if type(m.serial) = "roAssociativeArray" then serial = m.serial[port$] if type(serial) = "roSerialPort" then serial.SendByte(int(val(byteValue$))) endif endif else if command$ = "sendSerialBytesCommand" then portParameter = parameters["port"] port$ = portParameter.GetCurrentParameterValue() byteValueParameter = parameters["byteValues"] byteValues$ = byteValueParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendSerialBytesCommand " + byteValues$ + " to port " + port$) if type(m.serial) = "roAssociativeArray" then serial = m.serial[port$] if type(serial) = "roSerialPort" then byteString$ = StripLeadingSpaces(byteValues$) if len(byteString$) > 0 then commaPosition = -1 while commaPosition <> 0 commaPosition = instr(1, byteString$, ",") if commaPosition = 0 then serial.SendByte(val(byteString$)) else serial.SendByte(val(left(byteString$, commaPosition - 1))) endif byteString$ = mid(byteString$, commaPosition+1) end while endif endif endif else if command$ = "sendUDPCommand" then udpStringParameter = parameters["udpString"] udpString$ = udpStringParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Send UDP command " + udpString$) m.udpSender.Send(udpString$) else if command$ = "sendProntoIRRemote" then irRemoteOutParameter = parameters["irRemoteOut"] irRemoteOut$ = irRemoteOutParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Send Pronto IR Remote " + irRemoteOut$) if type(m.remote) <> "roIRRemote" then m.remote = CreateObject("roIRRemote") m.remote.SetPort(m.msgPort) endif if type(m.remote) = "roIRRemote" then m.remote.Send("PHC", irRemoteOut$) endif else if command$ = "sendIRRemote" then irRemoteOutParameter = parameters["irRemoteOut"] irRemoteOut$ = irRemoteOutParameter.GetCurrentParameterValue() if instr(1, irRemoteOut$, "b-") = 1 then irRemoteOut$ = mid(irRemoteOut$, 3) m.diagnostics.PrintDebug("Send Bose IR Remote " + irRemoteOut$) protocol$ = "Bose Sounddock" else m.diagnostics.PrintDebug("Send IR Remote " + irRemoteOut$) protocol$ = "NEC" endif if type(m.remote) <> "roIRRemote" then m.remote = CreateObject("roIRRemote") m.remote.SetPort(m.msgPort) endif if type(m.remote) = "roIRRemote" then irRemoteOut% = int(val(irRemoteOut$)) m.remote.Send(protocol$, irRemoteOut%) endif else if command$ = "sendBLC400Output" then controllerIndexParameter = parameters["controllerIndex"] controllerIndex$ = controllerIndexParameter.GetCurrentParameterValue() controllerIndex% = int(val(controllerIndex$)) if type(m.blcs[controllerIndex%]) = "roControlPort" then CHANNEL_CMD_INTENSITY% = &h1000 CHANNEL_CMD_BLINK% = &h1100 CHANNEL_CMD_BREATHE% = &h1200 CHANNEL_CMD_STROBE% = &h1300 CHANNEL_CMD_MARQUEE% = &h1400 ' blink mode enumeration BLINK_SPEED_SLOW% = &h20 BLINK_SPEED_MEDIUM% = &h21 BLINK_SPEED_FAST% = &h22 ' marquee sub commands MARQUEE_EXECUTE% = &h30 MARQUEE_ON_TIME% = &h31 MARQUEE_OFF_TIME% = &h32 MARQUEE_FADE_OUT% = &h33 MARQUEE_PLAYBACK% = &h34 MARQUEE_TRANSITION% = &h35 MARQUEE_INTENSITY% = &h36 ' marquee playback mode enumeration MARQUEE_PLAYBACK_LOOP% = &h40 MARQUEE_PLAYBACK_BOUNCE% = &h41 MARQUEE_PLAYBACK_ONCE% = &h42 MARQUEE_PLAYBACK_RANDOM% = &h43 ' marquee transition mode enumeration MARQUEE_TRANSITION_OFF% = &h50 MARQUEE_TRANSITION_FULL% = &h51 MARQUEE_TRANSITION_OVERLAP% = &h52 controlCmd = CreateObject("roArray", 4, false) effectParameter = parameters["effect"] effect$ = effectParameter.GetCurrentParameterValue() channelsParameter = parameters["channels"] channels$ = channelsParameter.GetCurrentParameterValue() channels% = int(val(channels$)) time% = GetIntegerParameterValue(parameters, "time", 0) intensity% = GetIntegerParameterValue(parameters, "intensity", 100) blinkRateParameter = parameters["blinkRate"] blinkRate$ = blinkRateParameter.GetCurrentParameterValue() minimumIntensity% = GetIntegerParameterValue(parameters, "minimumIntensity", 0) maximumIntensity% = GetIntegerParameterValue(parameters, "maximumIntensity", 100) controlCmd[0] = channels% if effect$ = "intensity" then controlCmd[0] = CHANNEL_CMD_INTENSITY% or channels% controlCmd[1] = time% ' time in seconds for transition (zero for instantaneous) controlCmd[2] = intensity% ' target intensity controlCmd[3] = 0 ' unused m.diagnostics.PrintDebug("sendBLC400Output - intensity: time = " + stri(time%) + " intensity = " + stri(intensity%)) else if effect$ = "blink" then if blinkRate$ = "fast" then blinkRate% = BLINK_SPEED_FAST% else if blinkRate$ = "medium" then blinkRate% = BLINK_SPEED_MEDIUM% else blinkRate% = BLINK_SPEED_SLOW% endif controlCmd[ 0 ] = CHANNEL_CMD_BLINK% or channels% controlCmd[ 1 ] = blinkRate% ' blink mode controlCmd[ 2 ] = 100 ' intensity (0 = use current value) controlCmd[ 3 ] = 0 ' unused m.diagnostics.PrintDebug("sendBLC400Output - blink: blinkRate = " + blinkRate$) else if effect$ = "breathe" then controlCmd[ 0 ] = CHANNEL_CMD_BREATHE% or channels% controlCmd[ 1 ] = time% ' time in seconds for change (zero for instantaneous) controlCmd[ 2 ] = minimumIntensity% ' min intensity (or rather starting intensity) controlCmd[ 3 ] = maximumIntensity% ' max intensity m.diagnostics.PrintDebug("sendBLC400Output - breathe: time = " + stri(time%) + " minimumIntensity = " + stri(minimumIntensity%) + " maximumIntensity = " + stri(maximumIntensity%)) else if effect$ = "strobe" then controlCmd[ 0 ] = CHANNEL_CMD_STROBE% or channels% controlCmd[ 1 ] = time% ' time in milliseconds for strobe controlCmd[ 2 ] = intensity% ' intensity (0 = use current value) controlCmd[ 3 ] = 0 ' unused m.diagnostics.PrintDebug("sendBLC400Output - strobe: time = " + stri(time%) + " intensity = " + stri(intensity%)) else if effect$ = "marquee" then lightOnTime% = GetIntegerParameterValue(parameters, "lightOnTime", 0) lightOffTime% = GetIntegerParameterValue(parameters, "lightOffTime", 0) transitionModeParameter = parameters["transitionMode"] transitionMode$ = transitionModeParameter.GetCurrentParameterValue() playbackModeParameter = parameters["playbackMode"] playbackMode$ = playbackModeParameter.GetCurrentParameterValue() if playbackMode$ = "loop" then playbackMode% = MARQUEE_PLAYBACK_LOOP% else if playbackMode$ = "backAndForth" then playbackMode% = MARQUEE_PLAYBACK_BOUNCE% else if playbackMode$ = "playOnce" then playbackMode% = MARQUEE_PLAYBACK_ONCE% else playbackMode% = MARQUEE_PLAYBACK_RANDOM% endif m.diagnostics.PrintDebug("sendBLC400Output - marquee: mode = " + playbackMode$) transitionMode% = MARQUEE_TRANSITION_OFF% if transitionMode$ = "hard" then fadeOut% = 0 else fadeOut% = 1 if transitionMode$ = "smoothFull" then transitionMode% = MARQUEE_TRANSITION_FULL% else if transitionMode$ = "smoothOverlap" transitionMode% = MARQUEE_TRANSITION_OVERLAP% endif endif controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% controlCmd[ 1 ] = MARQUEE_PLAYBACK% ' changing playback mode controlCmd[ 2 ] = playbackMode% ' playback mode controlCmd[ 3 ] = 0 ' unused m.blcs[controllerIndex%].SetOutputValues(controlCmd) controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% controlCmd[ 1 ] = MARQUEE_FADE_OUT% ' fadeOut controlCmd[ 2 ] = fadeOut% ' hard or soft controlCmd[ 3 ] = 0 ' unused m.blcs[controllerIndex%].SetOutputValues(controlCmd) controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% controlCmd[ 1 ] = MARQUEE_TRANSITION% controlCmd[ 2 ] = transitionMode% controlCmd[ 3 ] = 0 ' unused m.blcs[controllerIndex%].SetOutputValues(controlCmd) controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% controlCmd[ 1 ] = MARQUEE_ON_TIME% ' on time controlCmd[ 2 ] = lightOnTime% ' msec controlCmd[ 3 ] = 0 ' unused m.blcs[controllerIndex%].SetOutputValues(controlCmd) controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% controlCmd[ 1 ] = MARQUEE_OFF_TIME% ' off time controlCmd[ 2 ] = lightOffTime% ' msec controlCmd[ 3 ] = 0 ' unused m.blcs[controllerIndex%].SetOutputValues(controlCmd) controlCmd[ 0 ] = CHANNEL_CMD_MARQUEE% or channels% controlCmd[ 1 ] = MARQUEE_EXECUTE% ' marquee sub command controlCmd[ 2 ] = 0 ' unused controlCmd[ 3 ] = 0 ' unused endif m.blcs[controllerIndex%].SetOutputValues(controlCmd) endif else if command$ = "sendBPOutput" then buttonPanelIndexParameter = parameters["buttonPanelIndex"] buttonPanelIndex$ = buttonPanelIndexParameter.GetCurrentParameterValue() buttonPanelIndex% = int(val(buttonPanelIndex$)) buttonNumberParameter = parameters["buttonNumber"] buttonNumber$ = buttonNumberParameter.GetCurrentParameterValue() actionParameter = parameters["action"] action$ = actionParameter.GetCurrentParameterValue() if type(m.bpOutput[buttonPanelIndex%]) = "roControlPort" then m.diagnostics.PrintDebug("Apply action " + action$ + " to BP button " + buttonNumber$) buttonNumber% = int(val(buttonNumber$)) if buttonNumber% = -1 then for i% = 0 to 10 if action$ = "on" then m.bpOutput[buttonPanelIndex%].SetOutputState(i%, 1) else if action$ = "off" then m.bpOutput[buttonPanelIndex%].SetOutputState(i%, 0) else if action$ = "fastBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(i%, &h038e38c) else if action$ = "mediumBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(i%, &h03f03e0) else if action$ = "slowBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(i%, &h03ff800) endif next else if action$ = "on" then m.bpOutput[buttonPanelIndex%].SetOutputState(buttonNumber%, 1) else if action$ = "off" then m.bpOutput[buttonPanelIndex%].SetOutputState(buttonNumber%, 0) else if action$ = "fastBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(buttonNumber%, &h038e38c) else if action$ = "mediumBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(buttonNumber%, &h03f03e0) else if action$ = "slowBlink" then m.bpOutput[buttonPanelIndex%].SetOutputValue(buttonNumber%, &h03ff800) endif endif endif else if command$ = "synchronize" then synchronizeKeywordParameter = parameters["synchronizeKeyword"] synchronizeKeyword$ = synchronizeKeywordParameter.GetCurrentParameterValue() if m.sign.enableEnhancedSynchronization and m.sysInfo.modelSupportsEnhancedSynchronization then m.diagnostics.PrintDebug("Send synchronize command " + synchronizeKeyword$ + " using SyncManager.") syncManagerEvent = m.SyncManager.Synchronize(synchronizeKeyword$, 300) zoneHSM.syncInfo = CreateObject("roAssociativeArray") zoneHSM.syncInfo.SyncDomain = syncManagerEvent.GetDomain() zoneHSM.syncInfo.SyncId = syncManagerEvent.GetId() zoneHSM.syncInfo.SyncIsoTimestamp = syncManagerEvent.GetIsoTimestamp() else m.diagnostics.PrintDebug("Send synchronize command " + synchronizeKeyword$) m.udpSender.Send("pre-" + synchronizeKeyword$) preloadRequired = true if type(zoneHSM.preloadState) = "roAssociativeArray" then if zoneHSM.preloadedStateName$ = zoneHSM.preloadState.name$ then preloadRequired = false endif endif ' currently only support preload / synchronizing with images and videos if preloadRequired then zoneHSM.preloadState.PreloadItem() endif sleep(300) ' m.udpSender.Send("ply-" + synchronizeKeyword$) if type(m.udpReceiver) = "roDatagramReceiver" then udpReceiverExists = true m.udpReceiver = 0 else udpReceiverExists = false endif m.WaitForSyncResponse(synchronizeKeyword$) if udpReceiverExists then m.udpReceiver = CreateObject("roDatagramReceiver", m.udpReceivePort) m.udpReceiver.SetPort(m.msgPort) endif endif else if command$ = "sendZoneMessage" then m.diagnostics.PrintDebug("Execute sendZoneMessage command") zoneMessageParameter = parameters["zoneMessage"] sendZoneMessageParameter$ = zoneMessageParameter.GetCurrentParameterValue() ' send ZoneMessage message zoneMessageCmd = CreateObject("roAssociativeArray") zoneMessageCmd["EventType"] = "SEND_ZONE_MESSAGE" zoneMessageCmd["EventParameter"] = sendZoneMessageParameter$ m.msgPort.PostMessage(zoneMessageCmd) else if command$ = "sendPluginMessage" then m.diagnostics.PrintDebug("Execute sendPluginMessage command") pluginName = parameters["pluginName"] pluginMessageParameter = parameters["message"] pluginName$ = pluginName.GetCurrentParameterValue() sendPluginMessageParameter$ = pluginMessageParameter.GetCurrentParameterValue() ' send ZoneMessage message pluginMessageCmd = CreateObject("roAssociativeArray") pluginMessageCmd["EventType"] = "SEND_PLUGIN_MESSAGE" pluginMessageCmd["PluginName"] = pluginName$ pluginMessageCmd["PluginMessage"] = sendPluginMessageParameter$ m.msgPort.PostMessage(pluginMessageCmd) else if command$ = "resizeZone" then m.diagnostics.PrintDebug("Execute resizeZone command") zoneId$ = parameters["zone"].GetCurrentParameterValue() x% = int(val(parameters["x"].GetCurrentParameterValue())) y% = int(val(parameters["y"].GetCurrentParameterValue())) width% = int(val(parameters["width"].GetCurrentParameterValue())) height% = int(val(parameters["height"].GetCurrentParameterValue())) zone = m.GetZone(zoneId$) if type(zone) = "roAssociativeArray" then r = CreateObject("roRectangle", x%, y%, width%, height%) if type(zone.videoPlayer) = "roVideoPlayer" then zone.videoPlayer.SetRectangle(r) endif if type(zone.imagePlayer) = "roImageWidget" then zone.imagePlayer.SetRectangle(r) endif if type(zone.displayedHtmlWidget) = "roHtmlWidget" then zone.displayedHtmlWidget.SetRectangle(r) endif ' SetRectangle doesn't exist for canvasWidget ' if type(zone.canvasWidget) = "roHtmlWidget" then ' zone.canvasWidget.SetRectangle(r) ' endif endif else if command$ = "internalSynchronize" then m.diagnostics.PrintDebug("Execute internalSynchronize command") internalSyncParameter = parameters["synchronizeKeyword"] internalSyncParameter$ = internalSyncParameter.GetCurrentParameterValue() ' send InternalSyncPreload message internalSyncPreload = CreateObject("roAssociativeArray") internalSyncPreload["EventType"] = "INTERNAL_SYNC_PRELOAD" internalSyncPreload["EventParameter"] = internalSyncParameter$ m.msgPort.PostMessage(internalSyncPreload) ' send InternalSyncMasterPreload message internalSyncMasterPreload = CreateObject("roAssociativeArray") internalSyncMasterPreload["EventType"] = "INTERNAL_SYNC_MASTER_PRELOAD" internalSyncMasterPreload["EventParameter"] = internalSyncParameter$ m.msgPort.PostMessage(internalSyncMasterPreload) ' current state is zoneHSM.activeState activeState = zoneHSM.activeState if type(activeState) = "roAssociativeArray" then if type(activeState.internalSynchronizeEventsMaster) = "roAssociativeArray" then if type(activeState.internalSynchronizeEventsMaster[internalSyncParameter$]) = "roAssociativeArray" then transition = activeState.internalSynchronizeEventsMaster[internalSyncParameter$] nextState$ = transition.targetMediaState$ if nextState$ <> "" then zoneHSM.preloadState = zoneHSM.stateTable[nextState$] zoneHSM.preloadState.PreloadItem() endif endif endif endif else if command$ = "reboot" then m.diagnostics.PrintDebug("Reboot") RebootSystem() else if command$ = "sendUSBBinaryEtapBytesCommand" then byteValuesParameter = parameters["byteValues"] byteValues$ = byteValuesParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendUSBBinaryEtapBytesCommand " + byteValues$) if type(m.usbBinaryEtap) <> "roUsbBinaryEtap" then m.usbBinaryEtap = CreateObject("roUsbBinaryEtap", 0) endif if type(m.usbBinaryEtap) = "roUsbBinaryEtap" then ba = CreateObject("roByteArray") byteString$ = StripLeadingSpaces(byteValues$) if len(byteString$) > 0 then commaPosition = -1 while commaPosition <> 0 commaPosition = instr(1, byteString$, ",") if commaPosition = 0 then ba.push(val(byteString$)) else ba.push(val(left(byteString$, commaPosition - 1))) endif byteString$ = mid(byteString$, commaPosition+1) end while m.usbBinaryEtap.Send(ba) endif endif else if command$ = "cecDisplayOn" then m.diagnostics.PrintDebug("Display On") m.CecDisplayOn() else if command$ = "cecDisplayOff" then m.diagnostics.PrintDebug("Display Off") m.CecDisplayOff() else if command$ = "cecSendString" then cecCommandParameter = parameters["cecAsciiString"] cecCommand$ = cecCommandParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("cecSendString " + cecCommand$) m.SendCecCommand(cecCommand$) else if command$ = "cecPhilipsSetVolume" then volumeParameter = parameters["volume"] volume$ = volumeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set cec Philips volume to " + volume$) volume% = int(val(volume$)) m.CecPhilipsSetVolume(volume%) else if command$ = "pauseVideoCommand" then m.diagnostics.PrintDebug("Pause video") m.PauseVideo(zoneHSM) else if command$ = "resumeVideoCommand" then m.diagnostics.PrintDebug("Resume video") m.ResumeVideo(zoneHSM) else if command$ = "enablePowerSaveMode" then m.diagnostics.PrintDebug("Enable Power Save Mode") m.SetPowerSaveMode(true) else if command$ = "disablePowerSaveMode" then m.diagnostics.PrintDebug("Disable Power Save Mode") m.SetPowerSaveMode(false) else if command$ = "pause" then pauseTimeParameter = parameters["pauseTime"] pauseTime$ = pauseTimeParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Pause for " + pauseTime$ + " milliseconds") pauseTime% = int(val(pauseTime$)) sleep(pauseTime%) else if command$ = "setVariable" then variableNameParameter = parameters["variableName"] variableValueParameter = parameters["variableValue"] variableName$ = variableNameParameter.GetVariableName() variableValue$ = variableValueParameter.GetCurrentParameterValue() userVariable = m.GetUserVariable(variableName$) if type(userVariable) = "roAssociativeArray" then userVariable.SetCurrentValue(variableValue$, true) else m.diagnostics.PrintDebug("User variable " + variableName$ + " not found.") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, variableName$) endif else if command$ = "incrementVariable" then m.ChangeUserVariableValue(parameters, 1) else if command$ = "decrementVariable" then m.ChangeUserVariableValue(parameters, -1) else if command$ = "resetVariable" then m.ResetVariable(parameters) else if command$ = "resetVariables" then m.ResetVariables() else if command$ = "configureAudioResources" then m.diagnostics.PrintDebug("Configure Audio Resources") zoneHSM.ConfigureAudioResources() else if command$ = "updateDataFeed" then m.UpdateDataFeed(parameters) endif End Sub Sub ResetVariable(parameters As Object) variableNameParameter = parameters["variableName"] variableName$ = variableNameParameter.GetVariableName() userVariable = m.GetUserVariable(variableName$) if type(userVariable) = "roAssociativeArray" then userVariable.Reset(true) else m.diagnostics.PrintDebug("User variable " + variableName$ + " not found.") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, variableName$) endif End Sub Sub ChangeUserVariableValue(parameters As Object, delta% As Integer) variableNameParameter = parameters["variableName"] variableName$ = variableNameParameter.GetVariableName() userVariable = m.GetUserVariable(variableName$) if type(userVariable) = "roAssociativeArray" then currentValue% = val(userVariable.GetCurrentValue()) currentValue% = currentValue% + delta% userVariable.SetCurrentValue(StripLeadingSpaces(stri(currentValue%)), true) else m.diagnostics.PrintDebug("User variable " + variableName$ + " not found.") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND, variableName$) endif End Sub Sub ChangeRFChannel(zone As Object, channelDelta% As Integer) startingChannel% = zone.currentChannelIndex% ' loop unnecessary if we don't support ignoreChannel while true zone.currentChannelIndex% = zone.currentChannelIndex% + channelDelta% if zone.currentChannelIndex% < 0 then zone.currentChannelIndex% = m.scannedChannels.Count() - 1 endif if zone.currentChannelIndex% >= m.scannedChannels.Count() then zone.currentChannelIndex% = 0 endif return ' if not m.scannedChannels[zone.currentChannelIndex%].ignoreChannel return if zone.currentChannelIndex% = startingChannel% return end while End Sub Function STTopEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid return "IGNORED" End Function Function GetPoolFilePath(assetPoolFiles As Object, fileName$ As String) As String if type(assetPoolFiles) = "roAssetPoolFiles" then return assetPoolFiles.GetPoolFilePath(fileName$) else return fileName$ endif End Function 'endregion 'region Logging REM ******************************************************* REM ******************************************************* REM *************** ******************** REM *************** LOGGING OBJECT ******************** REM *************** ******************** REM ******************************************************* REM ******************************************************* REM REM construct a new logging BrightScript object REM Function newLogging() As Object logging = CreateObject("roAssociativeArray") logging.bsp = m logging.msgPort = m.msgPort logging.systemTime = m.systemTime logging.diagnostics = m.diagnostics logging.SetSystemInfo = SetSystemInfo logging.CreateLogFile = CreateLogFile logging.MoveExpiredCurrentLog = MoveExpiredCurrentLog logging.MoveCurrentLog = MoveCurrentLog logging.InitializeLogging = InitializeLogging logging.ReinitializeLogging = ReinitializeLogging logging.InitializeCutoverTimer = InitializeCutoverTimer logging.WritePlaybackLogEntry = WritePlaybackLogEntry logging.WriteEventLogEntry = WriteEventLogEntry logging.WriteStateLogEntry = WriteStateLogEntry logging.WriteDiagnosticLogEntry = WriteDiagnosticLogEntry logging.PushLogFile = PushLogFile logging.CutoverLogFile = CutoverLogFile logging.HandleTimerEvent = HandleLoggingTimerEvent logging.PushLogFilesOnBoot = PushLogFilesOnBoot logging.OpenOrCreateCurrentLog = OpenOrCreateCurrentLog logging.DeleteExpiredFiles = DeleteExpiredFiles logging.DeleteOlderFiles = DeleteOlderFiles logging.DeleteLogFiles = DeleteLogFiles logging.DeleteAllLogFiles = DeleteAllLogFiles logging.GetLogFiles = GetLogFiles logging.CopyAllLogFiles = CopyAllLogFiles logging.CopyLogFiles = CopyLogFiles logging.FlushLogFile = FlushLogFile logging.UpdateLogCounter = UpdateLogCounter logging.logFile = invalid logging.uploadLogFolder = "logs" logging.uploadLogArchiveFolder = "archivedLogs" logging.uploadLogFailedFolder = "failedLogs" logging.logFileUpload = invalid logging.playbackLoggingEnabled = false logging.eventLoggingEnabled = false logging.diagnosticLoggingEnabled = false logging.stateLoggingEnabled = false logging.uploadLogFilesAtBoot = false logging.uploadLogFilesAtSpecificTime = false logging.uploadLogFilesTime% = 0 logging.useDate = logging.systemTime.IsValid() return logging End Function Function UpdateLogCounter(logCounter$ As String, maxValue% As Integer, numDigits% As Integer, writeToRegistry As Boolean) As String logCounter% = val(logCounter$) logCounter% = logCounter% + 1 if logCounter% > maxValue% then logCounter% = 0 endif logCounter$ = StripLeadingSpaces(stri(logCounter%)) while len(logCounter$) < numDigits% logCounter$ = "0" + logCounter$ end while if writeToRegistry then m.bsp.WriteRegistrySetting("lc", logCounter$) m.bsp.registrySettings.logCounter$ = logCounter$ else WriteAsciiFile("logCounter.txt", logCounter$) endif return logCounter$ End Function Function CreateLogFile() As Object if not m.useDate then ' don't use date for file name, use log counter logCounter$ = ReadAsciiFile("logCounter.txt") if logCounter$ = "" then logCounter$ = "000000" endif localFileName$ = "BrightSignLog." + m.deviceUniqueID$ + "-" + logCounter$ + ".log" fileNameLogCounter$ = logCounter$ logCounter$ = m.UpdateLogCounter(logCounter$, 999999, 6, false) else ' use date for file name logCounter$ = m.bsp.registrySettings.logCounter$ dtLocal = m.systemTime.GetLocalDateTime() year$ = Right(stri(dtLocal.GetYear()), 2) month$ = StripLeadingSpaces(stri(dtLocal.GetMonth())) if len(month$) = 1 then month$ = "0" + month$ endif day$ = StripLeadingSpaces(stri(dtLocal.GetDay())) if len(day$) = 1 then day$ = "0" + day$ endif dateString$ = year$ + month$ + day$ logDate$ = m.bsp.registrySettings.logDate$ if logDate$ = "" or logCounter$ = "" then logCounter$ = "000" else if logDate$ <> dateString$ then logCounter$ = "000" endif logDate$ = dateString$ localFileName$ = "BrightSign" + "Log." + m.deviceUniqueID$ + "-" + dateString$ + logCounter$ + ".log" m.bsp.WriteRegistrySetting("ld", logDate$) m.bsp.registrySettings.logDate$ = logDate$ logCounter$ = m.UpdateLogCounter(logCounter$, 999, 3, true) endif fileName$ = "currentLog/" + localFileName$ logFile = CreateObject("roCreateFile", fileName$) m.diagnostics.PrintDebug("Create new log file " + localFileName$) t$ = chr(9) ' version header$ = "BrightSignLogVersion"+t$+"3" logFile.SendLine(header$) ' serial number header$ = "SerialNumber"+t$+m.deviceUniqueID$ logFile.SendLine(header$) ' log counter if not m.useDate then counterInHeader$ = "LogCounter" + t$ + fileNameLogCounter$ logFile.SendLine(counterInHeader$) endif ' group id if type(m.networking) = "roAssociativeArray" then if type(m.networking.currentSync) = "roSyncSpec" then header$ = "Account"+t$+m.networking.currentSync.LookupMetadata("server", "account") logFile.SendLine(header$) header$ = "Group"+t$+m.networking.currentSync.LookupMetadata("server", "group") logFile.SendLine(header$) endif endif ' timezone header$ = "Timezone"+t$+m.systemTime.GetTimeZone() logFile.SendLine(header$) ' timestamp of log creation header$ = "LogCreationTime"+t$+m.systemTime.GetLocalDateTime().GetString() logFile.SendLine(header$) ' ip address nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then currentConfig = nc.GetCurrentConfig() nc = invalid ipAddress$ = currentConfig.ip4_address header$ = "IPAddress"+t$+ipAddress$ logFile.SendLine(header$) endif ' fw version header$ = "FWVersion"+t$+m.firmwareVersion$ logFile.SendLine(header$) ' script version header$ = "ScriptVersion"+t$+m.autorunVersion$ logFile.SendLine(header$) ' custom script version header$ = "CustomScriptVersion"+t$+m.customAutorunVersion$ logFile.SendLine(header$) ' model header$ = "Model"+t$+m.deviceModel$ logFile.SendLine(header$) logFile.AsyncFlush() return logFile End Function Sub MoveExpiredCurrentLog() dtLocal = m.systemTime.GetLocalDateTime() currentDate$ = StripLeadingSpaces(stri(dtLocal.GetDay())) if len(currentDate$) = 1 then currentDate$ = "0" + currentDate$ endif listOfPendingLogFiles = MatchFiles("/currentLog", "*") for each file in listOfPendingLogFiles logFileDate$ = left(right(file, 9), 2) if logFileDate$ <> currentDate$ then sourceFilePath$ = "currentLog/" + file destinationFilePath$ = "logs/" + file CopyFile(sourceFilePath$, destinationFilePath$) DeleteFile(sourceFilePath$) endif next End Sub Sub MoveCurrentLog() listOfPendingLogFiles = MatchFiles("/currentLog", "*") for each file in listOfPendingLogFiles sourceFilePath$ = "currentLog/" + file destinationFilePath$ = "logs/" + file CopyFile(sourceFilePath$, destinationFilePath$) DeleteFile(sourceFilePath$) next End Sub Sub InitializeLogging(playbackLoggingEnabled As Boolean, eventLoggingEnabled As Boolean, stateLoggingEnabled As Boolean, diagnosticLoggingEnabled As Boolean, uploadLogFilesAtBoot As Boolean, uploadLogFilesAtSpecificTime As Boolean, uploadLogFilesTime% As Integer) m.loggingEnabled = playbackLoggingEnabled or eventLoggingEnabled or stateLoggingEnabled or diagnosticLoggingEnabled if m.loggingEnabled then CreateDirectory("logs") CreateDirectory("currentLog") CreateDirectory("archivedLogs") CreateDirectory("failedLogs") endif m.DeleteExpiredFiles() m.playbackLoggingEnabled = playbackLoggingEnabled m.eventLoggingEnabled = eventLoggingEnabled m.stateLoggingEnabled = stateLoggingEnabled m.diagnosticLoggingEnabled = diagnosticLoggingEnabled m.uploadLogFilesAtBoot = uploadLogFilesAtBoot m.uploadLogFilesAtSpecificTime = uploadLogFilesAtSpecificTime m.uploadLogFilesTime% = uploadLogFilesTime% m.uploadLogsEnabled = uploadLogFilesAtBoot or uploadLogFilesAtSpecificTime if m.uploadLogFilesAtBoot then m.PushLogFilesOnBoot() endif m.MoveExpiredCurrentLog() if m.loggingEnabled then m.OpenOrCreateCurrentLog() m.InitializeCutoverTimer() End Sub Sub ReinitializeLogging(playbackLoggingEnabled As Boolean, eventLoggingEnabled As Boolean, stateLoggingEnabled As Boolean, diagnosticLoggingEnabled As Boolean, uploadLogFilesAtBoot As Boolean, uploadLogFilesAtSpecificTime As Boolean, uploadLogFilesTime% As Integer) if playbackLoggingEnabled = m.playbackLoggingEnabled and eventLoggingEnabled = m.eventLoggingEnabled and stateLoggingEnabled = m.stateLoggingEnabled and diagnosticLoggingEnabled = m.diagnosticLoggingEnabled and uploadLogFilesAtBoot = m.uploadLogFilesAtBoot and uploadLogFilesAtSpecificTime = m.uploadLogFilesAtSpecificTime and uploadLogFilesTime% = m.uploadLogFilesTime% then return m.loggingEnabled = playbackLoggingEnabled or eventLoggingEnabled or stateLoggingEnabled or diagnosticLoggingEnabled if m.loggingEnabled then CreateDirectory("logs") CreateDirectory("currentLog") CreateDirectory("archivedLogs") CreateDirectory("failedLogs") endif if type(m.cutoverTimer) = "roTimer" then m.cutoverTimer.Stop() m.cutoverTimer = invalid endif m.playbackLoggingEnabled = playbackLoggingEnabled m.eventLoggingEnabled = eventLoggingEnabled m.stateLoggingEnabled = stateLoggingEnabled m.diagnosticLoggingEnabled = diagnosticLoggingEnabled m.uploadLogFilesAtBoot = uploadLogFilesAtBoot m.uploadLogFilesAtSpecificTime = uploadLogFilesAtSpecificTime m.uploadLogFilesTime% = uploadLogFilesTime% m.uploadLogsEnabled = uploadLogFilesAtBoot or uploadLogFilesAtSpecificTime if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" and m.loggingEnabled then m.OpenOrCreateCurrentLog() endif m.InitializeCutoverTimer() End Sub Sub InitializeCutoverTimer() if m.uploadLogFilesAtSpecificTime then hour% = m.uploadLogFilesTime% / 60 minute% = m.uploadLogFilesTime% - (hour% * 60) else hour% = 0 minute% = 0 endif m.cutoverTimer = CreateObject("roTimer") m.cutoverTimer.SetPort(m.msgPort) m.cutoverTimer.SetDate(-1, -1, -1) m.cutoverTimer.SetTime(hour%, minute%, 0) m.cutoverTimer.Start() End Sub Function CopyAllLogFiles(storagePath$ As String) As Boolean if type(m.logFile) = "roCreateFile" or type(m.logFile) = "roAppendFile" then m.logFile.Flush() endif ok = m.CopyLogFiles(storagePath$, "currentLog") if not ok return ok ok = m.CopyLogFiles(storagePath$, "logs") if not ok return ok ok = m.CopyLogFiles(storagePath$, "failedLogs") if not ok return ok ok = m.CopyLogFiles(storagePath$, "archivedLogs") return ok End Function Function CopyLogFiles(storagePath$ As String, folderName$ As String) listOfLogFiles = MatchFiles("/" + folderName$, "*") for each file in listOfLogFiles sourceFilePath$ = "/" + folderName$ + "/" + file destinationFilePath$ = storagePath$ + file ok = CopyFile(sourceFilePath$, destinationFilePath$) if not ok return ok next return true End Function Sub DeleteAllLogFiles() ' close the current log file before deleting if type(m.logFile) = "roCreateFile" or type(m.logFile) = "roAppendFile" then m.logFile.Flush() m.logFile = invalid endif m.DeleteLogFiles("currentLog") m.DeleteLogFiles("logs") m.DeleteLogFiles("failedLogs") m.DeleteLogFiles("archivedLogs") End Sub Sub DeleteLogFiles(folderName$ As String) listOfLogFiles = MatchFiles("/" + folderName$, "*") for each file in listOfLogFiles fullFilePath$ = "/" + folderName$ + "/" + file DeleteFile(fullFilePath$) next End Sub Sub DeleteExpiredFiles() if m.useDate then ' delete any files that are more than 10 days old dtExpired = m.systemTime.GetLocalDateTime() dtExpired.SubtractSeconds(60 * 60 * 24 * 10) ' look in the following folders ' logs ' failedLogs ' archivedLogs m.DeleteOlderFiles("logs", dtExpired) m.DeleteOlderFiles("failedLogs", dtExpired) m.DeleteOlderFiles("archivedLogs", dtExpired) else MAX_FILES_TO_KEEP = 60 ' get a list of all log files logFiles = CreateObject("roArray", 1, true) m.GetLogFiles("logs", logFiles) m.GetLogFiles("failedLogs", logFiles) m.GetLogFiles("archivedLogs", logFiles) ' sort them in ascending order sortedIndices = CreateObject("roArray", 1, true) SortItems(logFiles, sortedIndices) ' if the count is > than the number to keep, delete the first n in the list while sortedIndices.Count() > MAX_FILES_TO_KEEP fullFilePath$ = logFiles[sortedIndices[0]].fullFilePath$ m.diagnostics.PrintDebug("Delete log file " + fullFilePath$) DeleteFile(fullFilePath$) sortedIndices.shift() endwhile endif End Sub ' sorted indices is an array that can grow and has no values on entry ' items is an array of associative arrays Sub SortItems(logFiles As Object, sortedIndices As Object) ' initialize array with indices. for i% = 0 to logFiles.Count()-1 sortedIndices[i%] = i% next numItemsToSort% = logFiles.Count() for i% = numItemsToSort% - 1 to 1 step -1 for j% = 0 to i%-1 index0% = sortedIndices[j%] logCounter0% = logFiles[index0%].counter% index1% = sortedIndices[j%+1] logCounter1% = logFiles[index1%].counter% if logCounter0% > logCounter1% then k% = sortedIndices[j%] sortedIndices[j%] = sortedIndices[j%+1] sortedIndices[j%+1] = k% endif next next End Sub Sub GetLogFiles(folderName$ As String, logFiles As Object) listOfLogFiles = MatchFiles("/" + folderName$, "*") for each file in listOfLogFiles logFile = { } logFile.counter% = int(val(left(right(file, 7), 3))) logFile.fullFilePath$ = "/" + folderName$ + "/" + file logFiles.push(logFile) next End Sub Sub DeleteOlderFiles(folderName$ As String, dtExpired As Object) listOfLogFiles = MatchFiles("/" + folderName$, "*") for each file in listOfLogFiles year$ = "20" + left(right(file,13), 2) month$ = left(right(file,11), 2) day$ = left(right(file, 9), 2) dtFile = CreateObject("roDateTime") dtFile.SetYear(int(val(year$))) dtFile.SetMonth(int(val(month$))) dtFile.SetDay(int(val(day$))) if dtFile < dtExpired then fullFilePath$ = "/" + folderName$ + "/" + file m.diagnostics.PrintDebug("Delete expired log file " + fullFilePath$) DeleteFile(fullFilePath$) endif next End Sub Sub FlushLogFile() if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return m.logFile.Flush() End Sub Sub WritePlaybackLogEntry(zoneName$ As String, startTime$ As String, endTime$ As String, itemType$ As String, fileName$ As String) if not m.playbackLoggingEnabled then return if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return t$ = chr(9) m.logFile.SendLine("L=p"+t$+"Z="+zoneName$+t$+"S="+startTime$+t$+"E="+endTime$+t$+"I="+itemType$+t$+"N="+fileName$) m.logFile.AsyncFlush() End Sub Sub WriteStateLogEntry(stateMachine As Object, stateName$ As String, stateType$ As String) if not m.stateLoggingEnabled then return if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return timestamp$ = m.systemTime.GetLocalDateTime().GetString() t$ = chr(9) if type(stateMachine.lastStateName$) = "roString" then lastStateName$ = stateMachine.lastStateName$ lastEventType$ = stateMachine.lastEventType$ lastEventData$ = stateMachine.lastEventData$ else lastStateName$ = "" lastEventType$ = "" lastEventData$ = "" endif m.logFile.SendLine("L=s"+t$+"S="+stateName$+t$+"T="+timestamp$+t$+"Y="+stateType$+t$+"LS="+lastStateName$+t$+"LE="+lastEventType$+t$+"LD="+lastEventData$) m.logFile.AsyncFlush() End Sub Sub WriteEventLogEntry(stateMachine As Object, stateName$ As String, eventType$ As String, eventData$ As String, eventActedOn$ As String) if not (m.eventLoggingEnabled or m.stateLoggingEnabled) then return if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return timestamp$ = m.systemTime.GetLocalDateTime().GetString() if eventActedOn$ = "1" then stateMachine.lastStateName$ = stateName$ stateMachine.lastEventType$ = eventType$ stateMachine.lastEventData$ = eventData$ endif if m.eventLoggingEnabled then t$ = chr(9) m.logFile.SendLine("L=e"+t$+"S="+stateName$+t$+"T="+timestamp$+t$+"E="+eventType$+t$+"D="+eventData$+t$+"A="+eventActedOn$) m.logFile.AsyncFlush() endif End Sub Sub WriteDiagnosticLogEntry(eventId$ As String, eventData$ As String) if not m.diagnosticLoggingEnabled then return if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return timestamp$ = m.systemTime.GetLocalDateTime().GetString() t$ = chr(9) m.logFile.SendLine("L=d"+t$+"T="+timestamp$+t$+"I="+eventId$+t$+"D="+eventData$) m.logFile.AsyncFlush() End Sub Sub PushLogFile(forceUpload As Boolean) if type(m.networking) <> "roAssociativeArray" then return if not m.uploadLogsEnabled and not forceUpload then return ' files that failed to upload in the past were moved to a different folder. move them back to the appropriate folder so that the script can attempt to upload them again listOfFailedLogFiles = MatchFiles("/" + m.uploadLogFailedFolder, "*.log") for each file in listOfFailedLogFiles target$ = m.uploadLogFolder + "/" + file fullFilePath$ = m.uploadLogFailedFolder + "/" + file ok = MoveFile(fullFilePath$, target$) next m.networking.UploadLogFiles() End Sub Sub PushLogFilesOnBoot() m.MoveCurrentLog() m.PushLogFile(false) End Sub Sub HandleLoggingTimerEvent() m.CutoverLogFile(false) m.cutoverTimer.Start() End Sub Sub CutoverLogFile(forceUpload As Boolean) if type(m.logFile) <> "roCreateFile" and type(m.logFile) <> "roAppendFile" then return m.logFile.Flush() m.MoveCurrentLog() m.logFile = m.CreateLogFile() if forceUpload or m.uploadLogFilesAtSpecificTime then m.PushLogFile(forceUpload) endif m.DeleteExpiredFiles() End Sub Sub OpenOrCreateCurrentLog() ' if there is an existing log file for today, just append to it. otherwise, create a new one to use listOfPendingLogFiles = MatchFiles("/currentLog", "*") for each file in listOfPendingLogFiles fileName$ = "currentLog/" + file m.logFile = CreateObject("roAppendFile", fileName$) if type(m.logFile) = "roAppendFile" then m.diagnostics.PrintDebug("Use existing log file " + file) return endif next m.logFile = m.CreateLogFile() End Sub 'endregion 'region Hierarchical State Machine ' ************************************************* ' ' Hierarchical State Machine Implementation ' ' ************************************************* Function newHSM() As Object HSM = CreateObject("roAssociativeArray") HSM.Initialize = HSMInitialize HSM.Constructor = HSMConstructor HSM.Dispatch = HSMDispatch HSM.IsIn = HSMIsIn HSM.InitialPseudostateHandler = invalid HSM.ConstructorHandler = invalid HSM.newHState = newHState HSM.topState = invalid HSM.activeState = invalid return HSM End Function Sub HSMConstructor() if type(m.ConstructorHandler) = invalid then stop m.ConstructorHandler() End Sub Sub HSMInitialize() ' there is definitely some confusion here about the usage of both activeState and m.activeState stateData = CreateObject("roAssociativeArray") ' empty event used to get super states emptyEvent = CreateObject("roAssociativeArray") emptyEvent["EventType"] = "EMPTY_SIGNAL" ' entry event entryEvent = CreateObject("roAssociativeArray") entryEvent["EventType"] = "ENTRY_SIGNAL" ' init event initEvent = CreateObject("roAssociativeArray") initEvent["EventType"] = "INIT_SIGNAL" ' execute initial transition m.activeState = m.InitialPseudoStateHandler() ' if there is no activeState, the playlist is empty if type(m.activeState) <> "roAssociativeArray" return activeState = m.activeState ' start at the top state if type(m.topState) <> "roAssociativeArray" then stop sourceState = m.topState while true entryStates = CreateObject("roArray", 4, true) entryStateIndex% = 0 entryStates[0] = activeState ' target of the initial transition status$ = m.activeState.HStateEventHandler(emptyEvent, stateData) ' send an empty event to get the super state activeState = stateData.nextState m.activeState = stateData.nextState while (activeState.id$ <> sourceState.id$) ' walk up the tree until the current source state is hit entryStateIndex% = entryStateIndex% + 1 entryStates[entryStateIndex%] = activeState status$ = m.activeState.HStateEventHandler(emptyEvent, stateData) activeState = stateData.nextState m.activeState = stateData.nextState end while ' activeState = entryStates[0] ' restore the target of the initial transition while (entryStateIndex% >= 0) ' retrace the entry path in reverse (desired) order entryState = entryStates[entryStateIndex%] status$ = entryState.HStateEventHandler(entryEvent, stateData) entryStateIndex% = entryStateIndex% - 1 end while sourceState = entryStates[0] ' new source state is the current state status$ = sourceState.HStateEventHandler(initEvent, stateData) if status$ <> "TRANSITION" then m.activeState = sourceState return endif activeState = stateData.nextState m.activeState = stateData.nextState end while End Sub Sub HSMDispatch(event As Object) ' if there is no activeState, the playlist is empty if type(m.activeState) <> "roAssociativeArray" return stateData = CreateObject("roAssociativeArray") ' empty event used to get super states emptyEvent = CreateObject("roAssociativeArray") emptyEvent["EventType"] = "EMPTY_SIGNAL" ' entry event entryEvent = CreateObject("roAssociativeArray") entryEvent["EventType"] = "ENTRY_SIGNAL" ' exit event exitEvent = CreateObject("roAssociativeArray") exitEvent["EventType"] = "EXIT_SIGNAL" ' init event initEvent = CreateObject("roAssociativeArray") initEvent["EventType"] = "INIT_SIGNAL" t = m.activeState ' save the current state status$ = "SUPER" while (status$ = "SUPER") ' process the event hierarchically s = m.activeState status$ = s.HStateEventHandler(event, stateData) m.activeState = stateData.nextState 'if type(m.activeState) = "roAssociativeArray" then ' print "m.activeState set to " + m.activeState.id$ + "0" 'else ' print "m.activeState set to invalid 0" 'endif end while if (status$ = "TRANSITION") path = CreateObject("roArray", 4, true) path[0] = m.activeState ' save the target of the transition path[1] = t ' save the current state while (t.id$ <> s.id$) ' exit from the current state to the transition s status$ = t.HStateEventHandler(exitEvent, stateData) if status$ = "HANDLED" then status$ = t.HStateEventHandler(emptyEvent, stateData) endif t = stateData.nextState end while t = path[0] ' target of the transition ' s is the source of the transition if (s.id$ = t.id$) then ' check source == target (transition to self) status$ = s.HStateEventHandler(exitEvent, stateData) ' exit the source ip = 0 else status$ = t.HStateEventHandler(emptyEvent, stateData) ' superstate of target t = stateData.nextState if (s.id$ = t.id$) then ' check source == target->super ip = 0 ' enter the target else status$ = s.HStateEventHandler(emptyEvent, stateData) ' superstate of source if (stateData.nextState.id$ = t.id$) then ' check source->super == target->super status$ = s.HStateEventHandler(exitEvent, stateData) ' exit the source ip = 0 ' enter the target else if (stateData.nextState.id$ = path[0].id$) then ' check source->super == target status$ = s.HStateEventHandler(exitEvent, stateData) ' exit the source else ' check rest of source == target->super->super and store the entry path along the way iq = 0 ' indicate LCA not found ip = 1 ' enter target and its superstate path[1] = t ' save the superstate of the target t = stateData.nextState ' save source->super ' get target->super->super status$ = path[1].HStateEventHandler(emptyEvent, stateData) while (status$ = "SUPER") ip = ip + 1 path[ip] = stateData.nextState ' store the entry path if (stateData.nextState.id$ = s.id$) then ' is it the source? iq = 1 ' indicate that LCA found ip = ip - 1 ' do not enter the source status$ = "HANDLED" ' terminate the loop else ' it is not the source; keep going up status$ = stateData.nextState.HStateEventHandler(emptyEvent, stateData) endif end while if (iq = 0) then ' LCA not found yet status$ = s.HStateEventHandler(exitEvent, stateData) ' exit the source ' check the rest of source->super == target->super->super... iq = ip status = "IGNORED" ' indicate LCA not found while (iq >= 0) if (t.id$ = path[iq].id$) then ' is this the LCA? status = "HANDLED" ' indicate LCA found ip = iq - 1 ' do not enter LCA iq = -1 ' terminate the loop else iq = iq -1 ' try lower superstate of target endif end while if (status <> "HANDLED") then ' LCA not found yet? ' check each source->super->... for each target->super... status = "IGNORED" ' keep looping while (status <> "HANDLED") status$ = t.HStateEventHandler(exitEvent, stateData) if (status$ = "HANDLED") then status$ = t.HStateEventHandler(emptyEvent, stateData) endif t = stateData.nextState ' set to super of t iq = ip while (iq > 0) if (t.id$ = path[iq].id$) then ' is this the LCA? ip = iq - 1 ' do not enter LCA iq = -1 ' break inner status = "HANDLED" ' break outer else iq = iq - 1 endif end while end while endif endif endif endif endif endif ' retrace the entry path in reverse (desired) order... while (ip >= 0) status$ = path[ip].HStateEventHandler(entryEvent, stateData) ' enter path[ip] ip = ip - 1 end while t = path[0] ' stick the target into register */ m.activeState = t ' update the current state */ 'print "m.activeState set to " + m.activeState.id$ + "1" ' drill into the target hierarchy... status$ = t.HStateEventHandler(initEvent, stateData) m.activeState = stateData.nextState while (status$ = "TRANSITION") ' stop ip = 0 path[0] = m.activeState status$ = m.activeState.HStateEventHandler(emptyEvent, stateData) ' find superstate m.activeState = stateData.nextState 'print "m.activeState set to " + m.activeState.id$ + "2" while (m.activeState.id$ <> t.id$) ip = ip + 1 path[ip] = m.activeState status$ = m.activeState.HStateEventHandler(emptyEvent, stateData) ' find superstate m.activeState = stateData.nextState 'print "m.activeState set to " + m.activeState.id$ + "3" end while m.activeState = path[0] 'print "m.activeState set to " + m.activeState.id$ + "4" while (ip >= 0) status$ = path[ip].HStateEventHandler(entryEvent, stateData) ip = ip - 1 end while t = path[0] status$ = t.HStateEventHandler(initEvent, stateData) end while endif m.activeState = t ' set the new state or restore the current state 'print "m.activeState set to " + m.activeState.id$ + "5" End Sub Function HSMIsIn() As Boolean return false End Function Function newHState(bsp As Object, id$ As String) As Object HState = CreateObject("roAssociativeArray") HState.HStateEventHandler = invalid ' filled in by HState instance HState.stateMachine = m HState.bsp = bsp HState.superState = invalid ' filled in by HState instance HState.id$ = id$ return HState End Function 'endregion 'region Diagnostics REM ******************************************************* REM ******************************************************* REM *************** ******************** REM *************** DIAGNOSTICS OBJECT ******************** REM *************** ******************** REM ******************************************************* REM ******************************************************* REM REM construct a new diagnostics BrightScript object REM Function newDiagnostics(sysFlags As Object) As Object diagnostics = CreateObject("roAssociativeArray") diagnostics.debug = sysFlags.debugOn diagnostics.autorunVersion$ = "unknown" diagnostics.customAutorunVersion$ = "unknown" diagnostics.firmwareVersion$ = "unknown" diagnostics.systemTime = CreateObject("roSystemTime") diagnostics.systemLogDebug = sysFlags.systemLogDebugOn if diagnostics.systemLogDebug then diagnostics.systemLog = CreateObject("roSystemLog") endif diagnostics.UpdateDebugOn = UpdateDebugOn diagnostics.UpdateSystemLogDebugOn = UpdateSystemLogDebugOn diagnostics.PrintDebug = PrintDebug diagnostics.PrintTimestamp = PrintTimestamp diagnostics.SetSystemInfo = SetSystemInfo return diagnostics End Function Sub UpdateDebugOn(debugOn As Boolean) m.debug = debugOn End Sub Sub UpdateSystemLogDebugOn(systemLogDebug As Boolean) m.systemLogDebug = systemLogDebug if systemLogDebug and type(m.systemLog) <> "roSystemLog" then m.systemLog = CreateObject("roSystemLog") endif End Sub Sub PrintDebug(debugStr$ As String) if type(m) <> "roAssociativeArray" then stop if m.debug then print debugStr$ endif if m.systemLogDebug then m.systemLog.SendLine(debugStr$) endif return End Sub Sub PrintTimestamp() eventDateTime = m.systemTime.GetLocalDateTime() if m.debug then print eventDateTime.GetString() if m.systemLogDebug then m.systemLog.SendLine(eventDateTime.GetString()) endif return End Sub Sub SetSystemInfo(sysInfo As Object, diagnosticCodes As Object) m.autorunVersion$ = sysInfo.autorunVersion$ m.customAutorunVersion$ = sysInfo.customAutorunVersion$ m.firmwareVersion$ = sysInfo.deviceFWVersion$ m.deviceUniqueID$ = sysInfo.deviceUniqueID$ m.deviceModel$ = sysInfo.deviceModel$ m.deviceFamily$ = sysInfo.deviceFamily$ m.modelSupportsWifi = sysInfo.modelSupportsWifi m.enableLogDeletion = sysInfo.enableLogDeletion m.diagnosticCodes = diagnosticCodes return End Sub Function GetColor(colorAttrs As Object) As Integer alpha$ = colorAttrs["a"] alpha% = val(alpha$) red$ = colorAttrs["r"] red% = val(red$) green$ = colorAttrs["g"] green% = val(green$) blue$ = colorAttrs["b"] blue% = val(blue$) color_spec% = (alpha%*256*256*256) + (red%*256*256) + (green%*256) + blue% return color_spec% End Function Function GetHexColor(colorAttrs As Object) As String ba = CreateObject("roByteArray") ba[0] = val(colorAttrs["a"]) alpha$ = ba.ToHexString() ba[0] = val(colorAttrs["r"]) red$ = ba.ToHexString() ba[0] = val(colorAttrs["g"]) green$ = ba.ToHexString() ba[0] = val(colorAttrs["b"]) blue$ = ba.ToHexString() return alpha$ + red$ + green$ + blue$ End Function Function ByteArraysMatch(baInput As Object, baSpec As Object) As Boolean if baSpec.Count() > baInput.Count() return false numBytesToMatch% = baSpec.Count() numBytesInInput% = baInput.Count() startByteInInput% = numBytesInInput% - numBytesToMatch% for i% = 0 to baSpec.Count() - 1 if baInput[startByteInInput% + i%] <> baSpec[i%] return false next return true End Function Function StripLeadingSpaces(inputString$ As String) As String while true if left(inputString$, 1)<>" " then return inputString$ inputString$ = right(inputString$, len(inputString$)-1) endwhile return inputString$ End Function Function CopyDateTime(dateTimeIn As Object) As Object dateTimeOut = CreateObject("roDateTime") dateTimeOut.SetYear(dateTimeIn.GetYear()) dateTimeOut.SetMonth(dateTimeIn.GetMonth()) dateTimeOut.SetDay(dateTimeIn.GetDay()) dateTimeOut.SetHour(dateTimeIn.GetHour()) dateTimeOut.SetMinute(dateTimeIn.GetMinute()) dateTimeOut.SetSecond(dateTimeIn.GetSecond()) dateTimeOut.SetMillisecond(dateTimeIn.GetMillisecond()) return dateTimeOut End Function REM ******************************************************* REM ******************************************************* REM *************** ******************** REM *************** DIAGNOSTIC CODES ******************** REM *************** ******************** REM ******************************************************* REM ******************************************************* Function newDiagnosticCodes() As Object diagnosticCodes = CreateObject("roAssociativeArray") diagnosticCodes.EVENT_STARTUP = "1000" diagnosticCodes.EVENT_SYNCSPEC_RECEIVED = "1001" diagnosticCodes.EVENT_DOWNLOAD_START = "1002" diagnosticCodes.EVENT_FILE_DOWNLOAD_START = "1003" diagnosticCodes.EVENT_FILE_DOWNLOAD_COMPLETE = "1004" diagnosticCodes.EVENT_DOWNLOAD_COMPLETE = "1005" diagnosticCodes.EVENT_READ_SYNCSPEC_FAILURE = "1006" diagnosticCodes.EVENT_RETRIEVE_SYNCSPEC_FAILURE = "1007" diagnosticCodes.EVENT_NO_SYNCSPEC_AVAILABLE = "1008" diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_IMMEDIATE_FAILURE = "1009" diagnosticCodes.EVENT_FILE_DOWNLOAD_FAILURE = "1010" diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_FAILURE = "1011" diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE = "1012" diagnosticCodes.EVENT_LOGFILE_UPLOAD_FAILURE = "1013" diagnosticCodes.EVENT_SYNC_ALREADY_ACTIVE = "1014" diagnosticCodes.EVENT_CHECK_CONTENT = "1015" diagnosticCodes.EVENT_FILE_DOWNLOAD_PROGRESS = "1016" diagnosticCodes.EVENT_FIRMWARE_DOWNLOAD = "1017" diagnosticCodes.EVENT_SCRIPT_DOWNLOAD = "1018" diagnosticCodes.EVENT_BATTERY_STATUS = "1019" diagnosticCodes.EVENT_POWER_EVENT = "1020" diagnosticCodes.EVENT_USER_VARIABLE_NOT_FOUND = "1021" diagnosticCodes.EVENT_MEDIA_COUNTER_VARIABLE_NOT_FOUND = "1022" diagnosticCodes.EVENT_START_PRESENTATION = "1023" diagnosticCodes.EVENT_GPS_LOCATION = "1024" diagnosticCodes.EVENT_GPS_NOT_LOCKED = "1025" diagnosticCodes.EVENT_RETRIEVE_USER_VARIABLE_FEED = "1026" diagnosticCodes.EVENT_RETRIEVE_LIVE_TEXT_FEED = "1027" diagnosticCodes.EVENT_USER_VARIABLE_FEED_DOWNLOAD_FAILURE = "1028" diagnosticCodes.EVENT_LIVE_TEXT_FEED_DOWNLOAD_FAILURE = "1029" diagnosticCodes.EVENT_UNASSIGNED_LOCAL_PLAYLIST = "1030" diagnosticCodes.EVENT_UNASSIGNED_LOCAL_PLAYLIST_NO_NAVIGATION = "1031" diagnosticCodes.EVENT_REALIZE_FAILURE = "1032" diagnosticCodes.EVENT_LIVE_TEXT_PLUGIN_FAILURE = "1033" diagnosticCodes.EVENT_INVALID_DATE_TIME_SPEC = "1034" diagnosticCodes.EVENT_HTML5_LOAD_ERROR = "1035" diagnosticCodes.EVENT_USB_UPDATE_SECURITY_ERROR = "1036" diagnosticCodes.EVENT_TUNE_FAILURE = "1037" diagnosticCodes.EVENT_SCAN_START = "1038" diagnosticCodes.EVENT_CHANNEL_FOUND = "1039" diagnosticCodes.EVENT_SCAN_COMPLETE = "1040" diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE = "1041" diagnosticCodes.EVENT_DISK_ERROR = "1042" diagnosticCodes.EVENT_BLC400_STATUS = "1100" diagnosticCodes.EVENT_CONTINUE_LIVE_DATA_FEED_CONTENT_DOWNLOAD = "1200" diagnosticCodes.EVENT_RESTART_LIVE_DATA_FEED_CONTENT_DOWNLOAD = "1201" diagnosticCodes.EVENT_START_LIVE_DATA_FEED_CONTENT_DOWNLOAD = "1202" diagnosticCodes.EVENT_ASSETPOOL_UNPROTECT_FAILURE = "1203" diagnosticCodes.EVENT_PLAYBACK_FAILURE = "1204" diagnosticCodes.EVENT_START_MRSS_FEED_CONTENT_DOWNLOAD = "1205" diagnosticCodes.EVENT_UNABLE_TO_CREATE_ASSET_POOL = "1206" return diagnosticCodes End Function 'endregion 'region GPS Functions REM ================================================== REM GPS Functions REM ================================================== ' Parse the NMEA GPRMC format and return the data in an object - http://www.gpsinformation.org/dale/nmea.htm ' The returned object contains the following fields ' .valid - boolean - is the sentence is correctly formed, has the correct checksum and has the correct GPRMC header ' .fixTime - contains the string from the sentence that is the time the sample was taken - no processing is done on this ' .fixActive - boolean - does the latitude and longitude contain real data ' .latitude - float - signed degrees of the latitude ' .longitude - float - signed degrees of the longitude Sub ParseGPSdataGPRMCformat(NMEAsentence as string) as object gpsData = {} starLoc = instr(1, NMEAsentence, "*") if starLoc = 0 then gpsData.valid = false else if starLoc = len(NMEAsentence) - 2 then CalcChecksum = CalcChecksum (mid(NMEAsentence, 2, len(NMEAsentence)-4)) ba=CreateObject("roByteArray") ba.fromhexstring(mid(NMEAsentence, len(NMEAsentence)-1, 2)) CalcChecksum = ba[0] if (CalcChecksum <> CalcChecksum) then gpsData.valid = false else ' Strip off the beginning $ sign and the * + checksum strippedSentence = mid(NMEAsentence, 2, len(NMEAsentence)-4) ' Get the identifier field = getNextGPSfield(strippedSentence, 1) gpsData.type = field.fieldString ' Make sure this is the right data format if (gpsData.type <> "GPRMC") then gpsData.valid = false else gpsData.valid = true ' Get the fix time field = getNextGPSfield(strippedSentence, field.nextFieldStart) gpsData.fixTime = field.fieldString ' Get the status of the fix: A=Active, V=Void time, convert to fixActive = true for A, false for V field = getNextGPSfield(strippedSentence, field.nextFieldStart) if (field.fieldString <> "A") then gpsData.fixActive = false gpsData.latitude = 0 gpsData.longitude = 0 else gpsData.fixActive = true ' Get the Latitude field = getNextGPSfield(strippedSentence, field.nextFieldStart) latDegrees = val(left(field.fieldString,2)) latMinutes = val(mid(field.fieldString,3)) latDegrees = latDegrees + (latMinutes/60) ' Get the Latitude Direction field = getNextGPSfield(strippedSentence, field.nextFieldStart) ' Adjust the sign of the angle based on the direction gpsData.latitude = ConvertNSEWtoQuadrant(field.fieldString, latDegrees) ' Get the Longitude field = getNextGPSfield(strippedSentence, field.nextFieldStart) longDegrees = val(left(field.fieldString,3)) longMinutes = val(mid(field.fieldString,4)) longDegrees = longDegrees + (longMinutes/60) ' Get the Longitude Direction field = getNextGPSfield(strippedSentence, field.nextFieldStart) ' Adjust the sign of the angle based on the direction gpsData.longitude = ConvertNSEWtoQuadrant(field.fieldString, longDegrees) end if end if end if end if return gpsData end Sub ' Parse and return the next NMEA field from the sentence ' returns an object with two members: ' .fieldString - contains the contents of the field, if nothing is in the field - returns "" ' .nextFieldStart - indicates the location in the string where the next field should start Sub getNextGPSfield (NMEAsentence as string, startingIndex as integer) as object gpsField = {} ' Look for the next field seperator as a comma (this is the case except for the checksum which is a *) fieldEndLoc = instr(startingIndex, NMEAsentence, ",") if fieldEndLoc <> 0 then if fieldEndLoc > startingIndex then gpsField.fieldString = mid(NMEAsentence, startingIndex, fieldEndLoc-startingIndex) else gpsField.fieldString = "" end if gpsField.nextFieldStart = fieldEndLoc + 1 else stringLen = len(NMEAsentence) if (stringLen >= startingIndex) then gpsField.fieldString = mid(NMEAsentence, startingIndex, stringLen-startingIndex+1) else gpsField.fieldString = "" end if gpsField.nextFieldStart = stringLen + 1 end if return (gpsField) end Sub ' Calculate the great circle distance of two gps points - points must be in radians Sub CalcGPSDistance(lat1 as float, lon1 as float, lat2 as float, lon2 as float) as float radiusOfEarthInFeet# = 3963.1 * 5280.0 ' Convert coodinate 1 to Cartesian coordinates x1# = radiusOfEarthInFeet# * cos(lon1) * sin(lat1) y1# = radiusOfEarthInFeet# * sin(lon1) * sin(lat1) z1# = radiusOfEarthInFeet# * sin(lat1) ' Convert coodinate 2 to Cartesian coordinates x2# = radiusOfEarthInFeet# * cos(lon2) * sin(lat2) y2# = radiusOfEarthInFeet# * sin(lon2) * sin(lat2) z2# = radiusOfEarthInFeet# * sin(lat2) ' Calc the distance based on Euclidean distance distance = sqr((x1#-x2#)*(x1#-x2#) + (y1#-y2#)*(y1#-y2#) + (z1#-z2#)*(z1#-z2#)) return (distance) end sub ' Calculate the checksum based on the NMEA stardard - http://www.gpsinformation.org/dale/nmea.htm ' the checksum is an XOR of all characters between the $ and * in the sentence Sub CalcChecksum (theString as string) as integer checksum = 0 theStringLen = len (theString) if (theStringLen >= 2) then a = asc(mid(theString,1,1)) b = asc(mid(thestring,2,1)) ' XOR the two first two characters in the string checksum = &HFF AND ((a OR b) AND (NOT(a AND b))) else if (theStringLen = 1) then ' If only one character is in the string, it is the checksum checksum = asc(mid(theString,1,1)) end if if (theStringLen >= 3) then for i= 3 to theStringLen a = checksum b = asc(mid(thestring,i,1)) ' XOR the current checksum with the next character checksum = &HFF AND ((a OR b) AND (NOT(a AND b))) next end if return (checksum) end sub Sub ConvertDecimalDegtoRad(deg as float) as float pi = 3.14159265358979 radians = deg * (pi/180) return (radians) end Sub Sub ConvertNSEWtoQuadrant(direction as string, angle as float) as float if (direction = "W") or (direction = "w") or (direction = "S") or (direction = "s") then angle = angle * -1 end if return (angle) end sub 'endregion 'region SIGNCHANNEL / MEDIARSS REM ******************************************************* REM ******************************************************* REM *************** *************** REM *************** SIGNCHANNEL / MEDIARSS *************** REM *************** *************** REM ******************************************************* REM ******************************************************* Function isImage( item As Object ) As Boolean REM Default is the item is an image rv = TRUE if item.type = "video/mpeg" OR item.type = "video/mp4" OR item.medium = "video" or item.type = "audio/mpeg" or item.medium = "audio" then rv = false endif return rv End Function Function isAudio( item As Object ) As Boolean REM Default is the item is an image rv = false if item.type = "audio/mpeg" or item.medium = "audio" then rv = true endif return rv End Function REM ================================================================ REM helper_GetDuration REM ================================================================ REM REM Get duration attribute of a media:content sub element REM of RSS Item element. Duration is number of seconds for REM image to be displayed on screen. REM REM If no duration found sets default of 15. REM If duration < 5 seconds sets minimum of 5 REM Function helper_GetDuration( contentElement As Object ) As Integer duration = contentElement.GetAttributes()["duration"] if duration = Invalid then return 15 end if duration = Val(duration) ' if duration < 5 then ' duration = 5 ' end if return duration End Function Function helper_GetFileSize( contentElement As Object ) As Integer size = contentElement.GetAttributes()["fileSize"] if size = Invalid then return 0 end if fileSize = Val(size) return fileSize End Function Function helper_GetProbeData( contentElement As Object ) As String probe = contentElement.GetAttributes()["probe"] if probe = Invalid then return "" end if return probe End Function 'endregion 'region TripleUSB ' TripleUSB state handler function ' ' information that needs to be available to this function includes ' serial port object for Triple USB ' serial port object for selected Bose product ' product name for selected Bose product ' noise threshold ' Quiet transition ' Loud transition ' ' Entry ' Make GetAmbientNoise call ' Serial Line Event ' Serial line event - Noise response ' Parse noise value ' Compare to noise threshold ' SetVolume on Bose product ' If noise value <= noise threshold, execute Quiet transition ' Else if noise value > noise threshold, execute Loud transition ' Function STTripleUSBEventHandler(event As Object, stateData As Object) As Object stateData.nextState = invalid if type(event) = "roAssociativeArray" then ' internal message event if IsString(event["EventType"]) then if event["EventType"] = "ENTRY_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": entry signal") m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) m.tripleUSBSerialPort = m.bsp.serial[m.tripleUSBPort$] m.boseProductPort = m.bsp.serial[m.boseProductPort$] m.tripleUSBSerialPort.SendLine("v?") ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, "tripleUSB") return "HANDLED" else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif endif else if type(event) = "roStreamLineEvent" then m.bsp.diagnostics.PrintDebug("Noise response serial event " + event.GetString()) noiseResponse$ = event.GetString() m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "serial", noiseResponse$, "1") if len(noiseResponse$) = 5 then 'Get noise value noiseValue% = int(val(mid(noiseResponse$, 3))) 'send out volume command if m.SendVolumeCommand(noiseValue%) = "HANDLED" then return "HANDLED" endif if noiseValue% <= m.noiseThreshold% then return m.ExecuteTransition(m.quietUserEvent, stateData, "") else return m.ExecuteTransition(m.loudUserEvent, stateData, "") endif else m.tripleUSBSerialPort.SendLine("v?") endif return "HANDLED" endif stateData.nextState = m.superState return "SUPER" End Function Function SendVolumeCommand(noiseValue% As Integer) As Object if m.boseProductName$ = "" then return "" endif 'lookup noise value in our volume table for the product and send vol command m.bsp.diagnostics.PrintDebug("SendVolumeCommand: enter") m.bsp.diagnostics.PrintDebug("noiseval " + str(noiseValue%) ) 'perform linear interpolation volume% = 0 'protect against no volume table in the BoseProducts xml file if type(m.volumeTable) <> "roAssociativeArray" then m.bsp.diagnostics.PrintDebug("No Bose Product Volume Table found!" ) return "HANDLED" endif if noiseValue% >= m.volumeTable.xval3% then volume% = m.volumeTable.yval3% else if noiseValue% >= m.volumeTable.xval2% then if m.volumeTable.xval3% - m.volumeTable.xval2% = 0 then m.bsp.diagnostics.PrintDebug("Divide by Zero will occur, check volume table info" ) return "HANDLED" endif volume% = (noiseValue% - m.volumeTable.xval2%)*(m.volumeTable.yval3% - m.volumeTable.yval2%)/(m.volumeTable.xval3% - m.volumeTable.xval2%) + m.volumeTable.yval2% else if noiseValue% >= m.volumeTable.xval1% then if m.volumeTable.xval2% - m.volumeTable.xval1% = 0 then m.bsp.diagnostics.PrintDebug("Divide by Zero will occur, check volume table info" ) return "HANDLED" endif volume% = (noiseValue% - m.volumeTable.xval1%)*(m.volumeTable.yval2% - m.volumeTable.yval1%)/(m.volumeTable.xval2% - m.volumeTable.xval1%) + m.volumeTable.yval1% else volume% = m.volumeTable.yval1% endif m.bsp.diagnostics.PrintDebug("volume " + str(volume%) ) ' send volume command to Bose product if m.boseProductName$ = "Chihuahua" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Chihuahua") 'ask Ted about this, its giving me a string of 3 digits back with first one as a space? m.boseProductPort.SendLine("vo " + mid(str(volume%),2)) else if m.boseProductName$ = "Hershey" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Hershey") 'have to subract our volume from 100 since its an attenuation volume% = 100 - volume% m.boseProductPort.SendLine("VO CB " + mid(str(volume%),2)) else if m.boseProductName$ = "Onyx" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Onyx") 'ask Ted about this, its giving me a string of 3 digits back with first one as a space? m.boseProductPort.SendLine("vo " + mid(str(volume%),2)) else if m.boseProductName$ = "Cinnamon" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Cinnamon") m.boseProductPort.SendLine("vo " + mid(str(volume%),2)) else if m.boseProductName$ = "Whippit" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Whippit") m.boseProductPort.SendLine("vo " + mid(str(volume%),2)) else if m.boseProductName$ = "Reframe" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Reframe") m.boseProductPort.SendLine("VS" + mid(str(volume%),2)) else if m.boseProductName$ = "Max" then m.bsp.diagnostics.PrintDebug("Send Volume set command to Max") m.boseProductPort.SendLine("SP 102," + mid(str(volume%),2)) else m.bsp.diagnostics.PrintDebug("Unknown Bose Product: " + m.boseProductName$) return "HANDLED" endif return "" End Function 'endregion 'region Miscellaneous functions Function ReadSyncSpec() As Object currentSync = CreateObject("roSyncSpec") if type(currentSync) = "roSyncSpec" then if not currentSync.ReadFromFile("current-sync.xml") then if not currentSync.ReadFromFile("local-sync.xml") then if not currentSync.ReadFromFile("localToBSN-sync.xml") then currentSync = invalid endif endif endif endif return currentSync End Function Function FileExists(filePath$ As String) As Boolean file = CreateObject("roReadFile", filePath$) if not type(file) = "roReadFile" return false endif file = invalid return true End Function 'endregion ' region MRSSDataFeed methods ' check for existence of the feed file associated with this feed ' if it exists, setup asset collection / assetPoolFiles objects for the feed (independent of whether or not the assets are actually on the card) Sub ReadMRSSContent() onedelcache("\feed_cache") feedFileName$ = "feed_cache/" + m.name$ + ".xml" m.isMRSSFeed = m.FeedIsMRSS( feedFileName$ ) if not m.isMRSSFeed return m.bsp.diagnostics.PrintDebug("Read existing content for feed " + m.name$ + ".") file = CreateObject("roReadFile", feedFileName$) if type(file) <> "roReadFile" return file = invalid ' parse the feed, building an asset collection and a list of file items m.assetCollection = CreateObject("roAssetCollection") m.ParseMRSSFeed(feedFileName$) for each item in m.feed.items asset = CreateObject("roAssociativeArray") asset.link = item.url ' SignChannel sizes appear to be inaccurate ' if item.size > 0 then ' asset.size = item.size ' endif asset.name = item.url asset.change_hint = item.guid m.assetCollection.AddAsset(asset) next if not m.bsp.feedPool.ProtectAssets("display-" + m.name$, m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif m.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.feedPool, m.assetCollection) End Sub Function FeedIsMRSS( fileName$ As String ) xml = ReadAsciiFile( fileName$ ) if len(xml) = 0 return false feedXML = CreateObject("roXMLElement") if not feedXML.Parse(xml) return false if feedXML.HasAttribute("xmlns:media") then attrs = feedXML.GetAttributes() if attrs["xmlns:media"] = "http://search.yahoo.com/mrss/" return true endif return false End Function Sub DownloadMRSSContent() m.bsp.diagnostics.PrintDebug("DownloadMRSSContent") if type(m.assetFetcher) = "roAssetFetcher" then return endif fileNameOnCard$ = "feed_cache/" + m.name$ + ".xml" ' write the mrss feed to the card CopyFile(m.rssFileName$, fileNameOnCard$) m.bsp.diagnostics.PrintDebug("Download new content for feed " + m.name$ + ".") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_START_MRSS_FEED_CONTENT_DOWNLOAD, m.name$) ' parse the feed, building an asset collection and a list of file items m.assetCollection = CreateObject("roAssetCollection") m.ParseMRSSFeed(m.rssFileName$) for each item in m.feed.items asset = CreateObject("roAssociativeArray") asset.link = item.url ' SignChannel sizes are inaccurate ' if item.size > 0 then ' asset.size = item.size ' endif asset.name = item.url asset.change_hint = item.guid m.assetCollection.AddAsset(asset) next if not m.bsp.feedPool.ProtectAssets("download-" + m.name$, m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif m.assetFetcher = CreateObject("roAssetFetcher", m.bsp.feedPool) m.assetFetcher.SetPort(m.bsp.msgPort) m.assetFetcher.SetMinimumTransferRate(1000,60) m.assetFetcher.SetFileProgressIntervalSeconds(5) m.assetFetcher.SetUserData(m.name$) binding% = GetBinding(m.bsp.mediaFeedsXfersEnabledWired, m.bsp.mediaFeedsXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for assetFetcher is " + stri(binding%)) ok = m.assetFetcher.BindToInterface(binding%) if not ok then stop if not m.assetFetcher.AsyncDownload(m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_IMMEDIATE_FAILURE, m.assetFetcher.GetFailureReason()) m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### AsyncDownload failed: " + m.assetFetcher.GetFailureReason()) m.assetFetcher = invalid endif ' tell the states to switch over to the new spec immediately m.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.feedPool, m.assetCollection) mrssSpecUpdatedEvent = CreateObject("roAssociativeArray") mrssSpecUpdatedEvent["EventType"] = "MRSS_SPEC_UPDATED" mrssSpecUpdatedEvent["LiveDataFeed"] = m m.bsp.msgPort.PostMessage(mrssSpecUpdatedEvent) End Sub Function newMRSSFeed( liveDataFeed As Object ) As Object feed = {} feed.liveDataFeed = liveDataFeed feed.ttlSeconds = -1 feed.PopulateFeedItems = mrssFeed_PopulateFeedItems feed.SetTTLMinutes = mrssFeed_SetTTLMinutes feed.ContentExists = mrssFeed_ContentExists return feed End Function Sub ParseMRSSFeed( filePath$ As String ) m.feed = newMRSSFeed( m ) m.feed.PopulateFeedItems( filePath$ ) ' if the ttl specified in the feed < the update interval, set the update interval to the specified ttl if m.feed.ttlSeconds > 0 and m.feed.ttlSeconds < m.updateInterval% then m.updateInterval% = m.feed.ttlSeconds endif End Sub Function mrssFeed_ContentExists( assetPoolFiles As Object ) As Boolean for each item in m.items file$ = item.url filePath$ = GetPoolFilePath(assetPoolFiles, file$) if filePath$ <> "" return true next return false End Function Sub mrssFeed_PopulateFeedItems( filePath$ As String ) m.items = [] xml = ReadAsciiFile( filePath$ ) if len(xml) = 0 return mrssFeedXML = CreateObject("roXMLElement") if not mrssFeedXML.Parse(xml) stop for each elt in mrssFeedXML.GetBody().Peek().GetBody() name = elt.GetName() if name = "ttl" then m.SetTTLMinutes( elt.GetBody() ) else if name = "frameuserinfo:playtime" Then m.playtime = Val(elt.GetBody()) else if name = "title" then m.title = elt.GetBody() else if elt.GetName() = "item" then item = newMRSSItem(elt) if (item <> invalid) then m.items.Push( item ) end if end if next End Sub Sub mrssFeed_SetTTLMinutes( ttl As String ) if ttl = invalid or Val(ttl) <= 0 then m.ttlSeconds = -1 else if Val(ttl) < 2 then m.ttlSeconds = 120 else m.ttlSeconds = Val(ttl) * 60 end if ' the ttl is the lower of the ttl specified in the feed and the update rate of the live data if type( m.liveDataFeed ) = "roAssociativeArray" and type( m.liveDataFeed.updateInterval% ) = "roInt" and m.liveDataFeed.updateInterval% < m.ttlSeconds then m.ttlSeconds = m.liveDataFeed.updateInterval% endif End Sub Function newMRSSItem( xml as Object ) As Object item = { guid:"placeholder", durationSeconds: 60, url:"no_url", category:"no_category", thumbnail:"no_thumbnail", title:"no_title", displayStart:0, medium:"no_medium", size:0} contentPresent = false for each elt in xml.GetBody() name = elt.GetName() if name = "guid" then item.guid = elt.GetBody() else if name = "title" then item.title = elt.GetBody() else if name = "description" then item.description = elt.GetBody() else if name = "media:content" then item.url= elt.GetAttributes()["url"] item.type = elt.GetAttributes()["type"] item.duration = helper_GetDuration( elt ) item.size = helper_GetFileSize(elt) item.medium = elt.GetAttributes()["medium"] contentPresent = true item.probeData = helper_GetProbeData(elt) else if name = "media:thumbnail" then item.thumbnail = elt.GetAttributes()["url"] else if name = "category" then item.category = elt.GetBody() else if name = "media:group" then for each eltmg in elt.GetBody() name = eltmg.GetName() if name = "media:content" then item.url= eltmg.GetAttributes()["url"] item.type = eltmg.GetAttributes()["type"] item.duration = helper_GetDuration( eltmg ) item.size = helper_GetFileSize(eltmg) item.medium = eltmg.GetAttributes()["medium"] contentPresent = true item.probeData = helper_GetProbeData(eltmg) else if name = "media:thumbnail" then item.thumbnail = eltmg.GetAttributes()["url"] end if next end if next ' make item.guid the hash of the guid if not IsString(item.guid) then item.guid = StripLeadingSpaces(str(rnd(100000))) endif hashGen = CreateObject("roHashGenerator", "SHA1") item.guid = hashGen.hash(item.guid).ToHexString() hashGen = invalid if (contentPresent) then return item else return invalid end if End Function 'endregion 'region LiveDataFeed methods Sub ReadFeedContent() if m.downloadContent then m.ReadLiveFeedContent() else m.ReadMRSSContent() endif End Sub Sub ReadLiveFeedContent() filePath$ = "feed_cache/" + m.name$ + ".xml" parser = CreateObject("roRssParser") ok = parser.ParseFile(filePath$) if ok then m.fileKeys = CreateObject("roArray", 1, true) m.fileUrls = CreateObject("roArray", 1, true) assetCollection = CreateObject("roAssetCollection") ' parse the data feed; build data structures so that script can check if all content exists on the card article = parser.GetNextArticle() while type(article) = "roRssArticle" key = article.GetTitle() m.fileKeys.Push(key) url = article.GetDescription() m.fileUrls.Push(url) asset = CreateObject("roAssociativeArray") asset.link = url asset.name = url assetCollection.AddAsset(asset) article = parser.GetNextArticle() endwhile ' verify that all specified files are actually on the card m.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.feedPool, assetCollection) for each url in m.fileUrls filePath$ = m.assetPoolFiles.GetPoolFilePath(url) if filePath$ = "" then m.assetPoolFiles = invalid m.fileKeys = invalid m.fileUrls = invalid return endif next ' protect these assets if not m.bsp.feedPool.ProtectAssets("current-" + m.name$, assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif ' post message indicating load complete contentDataFeedLoaded = CreateObject("roAssociativeArray") contentDataFeedLoaded["EventType"] = "CONTENT_DATA_FEED_LOADED" contentDataFeedLoaded["Name"] = m.name$ m.bsp.msgPort.PostMessage(contentDataFeedLoaded) endif End Sub Function ContentDataFeedsIdentical(articlesFeed1 As Object, articlesFeed2 As Object, articleTitlesFeed1 As Object, articleTitlesFeed2 As Object) As Boolean if articlesFeed1.Count() = articlesFeed2.Count() then for i% = 0 to articlesFeed1.Count() - 1 if articleTitlesFeed1[i%] <> articleTitlesFeed2[i%] or articlesFeed1[i%] <> articlesFeed2[i%] then return false exit for endif next return true endif return false End Function Sub DownloadLiveFeedContent() m.bsp.diagnostics.PrintDebug("DownloadLiveFeedContent") if type(m.assetFetcher) = "roAssetFetcher" then ' fetch active, see if feed has changed if ContentDataFeedsIdentical(m.articlesDownloading, m.articles, m.articleTitlesDownloading, m.articleTitles) then m.bsp.diagnostics.PrintDebug("### live data feed asset fetch active and there are no changes to the feed spec so we'll let it continue") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_CONTINUE_LIVE_DATA_FEED_CONTENT_DOWNLOAD, "") return endif ' feed has changed, cancel download and start new download m.bsp.diagnostics.PrintDebug("### asset fetch active but feed has changed - cancel current download") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_RESTART_LIVE_DATA_FEED_CONTENT_DOWNLOAD, "") m.assetFetcher.AsyncCancel() m.assetFetcher = invalid endif ' it is only necessary to download content if ... ' there is no current content OR ' current keys are different from keys specified in new feed OR ' current content is different from content specified in new feed if type(m.assetPoolFiles) = "roAssetPoolFiles" and type(m.fileUrls) = "roArray" and m.fileUrls.Count() > 0 then ' there is current content; check to see if it matches the new feed ' compare to existing keys and Urls if ContentDataFeedsIdentical(m.fileUrls, m.articles, m.fileKeys, m.articleTitles) then m.bsp.diagnostics.PrintDebug("No change in content feed " + m.name$ + ". No need to download content.") return endif endif ' write the rss feed to the card CopyFile(m.rssFileName$, "feed_cache/" + m.name$ + ".xml") m.bsp.diagnostics.PrintDebug("Download new content for feed " + m.name$ + ".") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_START_LIVE_DATA_FEED_CONTENT_DOWNLOAD, m.name$) ' parse the feed, building an asset collection and a list of file items m.assetCollection = CreateObject("roAssetCollection") for each url in m.articles asset = CreateObject("roAssociativeArray") asset.link = url asset.name = url m.assetCollection.AddAsset(asset) next if not m.bsp.feedPool.ProtectAssets("new-" + m.name$, m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif m.assetFetcher = CreateObject("roAssetFetcher", m.bsp.feedPool) m.assetFetcher.SetPort(m.bsp.msgPort) m.assetFetcher.SetMinimumTransferRate(1000,60) m.assetFetcher.SetFileProgressIntervalSeconds(5) m.assetFetcher.SetUserData(m.name$) binding% = GetBinding(m.bsp.mediaFeedsXfersEnabledWired, m.bsp.mediaFeedsXfersEnabledWireless) m.bsp.diagnostics.PrintDebug("### Binding for assetFetcher is " + stri(binding%)) ok = m.assetFetcher.BindToInterface(binding%) if not ok then stop m.articlesDownloading = m.articles m.articleTitlesDownloading = m.articleTitles if not m.assetFetcher.AsyncDownload(m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_IMMEDIATE_FAILURE, m.assetFetcher.GetFailureReason()) m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### AsyncDownload failed: " + m.assetFetcher.GetFailureReason()) m.assetFetcher = invalid endif End Sub Sub RestartLiveDataFeedDownloadTimer(timespan% As Integer) if timespan% > 0 then ' set a timer to update live data feed if type(m.timer) = "roTimer" then m.timer.Stop() else m.timer = CreateObject("roTimer") m.timer.SetPort(m.bsp.msgPort) endif m.timer.SetElapsed(timespan%, 0) m.timer.Start() ' how to do this - m is the PlayerStateMachine m.bsp.liveDataFeedsByTimer.AddReplace(stri(m.timer.GetIdentity()), m) endif End Sub Sub HandleLiveDataFeedContentDownloadAssetFetcherEvent(event) POOL_EVENT_FILE_DOWNLOADED = 1 POOL_EVENT_FILE_FAILED = -1 POOL_EVENT_ALL_DOWNLOADED = 2 POOL_EVENT_ALL_FAILED = -2 m.bsp.diagnostics.PrintTimestamp() m.bsp.diagnostics.PrintDebug("### LiveDataFeedContentDownloadAssetFetcherEvent") if (event.GetEvent() = POOL_EVENT_FILE_DOWNLOADED) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_FILE_DOWNLOAD_COMPLETE, event.GetName()) m.bsp.diagnostics.PrintDebug("### File downloaded " + event.GetName()) ' track download traffic for dynamic playlists if m.isDynamicPlaylist and type(m.bsp.networkingHSM) = "roAssociativeArray" and type(m.assetPoolFiles) = "roAssetPoolFiles" then fileName$ = event.GetName() filePath$ = m.assetPoolFiles.GetPoolFilePath(fileName$) if filePath$ <> "" then checkFile = CreateObject("roReadFile", filePath$) if (checkFile <> invalid) then checkFile.SeekToEnd() size = checkFile.CurrentPosition() checkFile = invalid m.bsp.networkingHSM.UploadMRSSTrafficDownload(size) endif endif endif else if (event.GetEvent() = POOL_EVENT_FILE_FAILED) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_FILE_DOWNLOAD_FAILURE, event.GetName() + chr(9) + event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### File failed " + event.GetName() + ": " + event.GetFailureReason()) ' count number of failure and cancel if there are too many?? else if (event.GetEvent() = POOL_EVENT_ALL_FAILED) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SYNCSPEC_DOWNLOAD_FAILURE, event.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### Download failed: " + event.GetFailureReason()) m.assetFetcher = invalid m.RestartLiveDataFeedDownloadTimer(30) else if (event.GetEvent() = POOL_EVENT_ALL_DOWNLOADED) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_DOWNLOAD_COMPLETE, "") m.bsp.diagnostics.PrintDebug("### All files downloaded") m.assetFetcher = invalid if not m.isMRSSFeed then ' unprotect old assets, keep protection on new (now current) assets if not m.bsp.feedPool.ProtectAssets("current-" + m.name$, m.assetCollection) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.logging.FlushLogFile() m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + m.bsp.feedPool.GetFailureReason()) stop endif if not m.bsp.feedPool.UnprotectAssets("new-" + m.name$) then m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_ASSETPOOL_PROTECT_FAILURE, m.bsp.feedPool.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### UnprotectFiles failed: " + m.bsp.feedPool.GetFailureReason()) endif ' get asset pool m.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.feedPool, m.assetCollection) ' copy result of previous parse m.fileKeys = m.articleTitles m.fileUrls = m.articles ' post message indicating load complete contentDataFeedLoaded = CreateObject("roAssociativeArray") contentDataFeedLoaded["EventType"] = "CONTENT_DATA_FEED_LOADED" contentDataFeedLoaded["Name"] = m.name$ m.bsp.msgPort.PostMessage(contentDataFeedLoaded) else ' post message indicating load complete mrssDataFeedLoaded = CreateObject("roAssociativeArray") mrssDataFeedLoaded["EventType"] = "MRSS_DATA_FEED_LOADED" mrssDataFeedLoaded["Name"] = m.name$ m.bsp.msgPort.PostMessage(mrssDataFeedLoaded) endif endif End Sub Function clean_Initialize(msgPort As Object, bsp as Object) clean = newclean(msgPort, bsp) return clean End Function Function newclean(msgPort As Object, bsp as Object) s = {} s.version = 0.7 s.msgPort = msgPort s.bsp = bsp s.ProcessEvent = clean_ProcessEvent s.getStorage = clean_getStorage s.checkmyutil = clean_checkmyutil s.needToClear = clean_NeedToClear s.deltree = clean_deltree s.settimer = clean_settimer s.utilization = 1 s.utilmin = 25 s.filesystem = "vfat" s.objectName = "clean_object" s.debug = false s.cleantime%=23 'hour that clean plugin should run (11pm) s.settimer() return s End Function Function clean_ProcessEvent(event As Object) as boolean retval = false if type(event) = "roAssociativeArray" then if type(event["EventType"]) = "roString" then if event["EventType"] = "SEND_ZONE_MESSAGE" then sendZoneMessageParameter$ = event["EventParameter"] if ucase(sendZoneMessageParameter$) = "CLEAN" then m.getStorage() m.needToClear() endif endif endif end if if type(event) = "roTimerEvent" then timerIdentity = event.GetSourceIdentity() if m.mytimer <> invalid then if m.mytimer.GetIdentity() = timerIdentity then m.getStorage() m.needToClear() m.settimer() endif endif endif return retval End Function Sub clean_NeedToClear() If m.utilization > 70 then m.bsp.logging.WriteDiagnosticLogEntry("CardUsage", str(m.utilization)+"% - Deleting Files...") m.deltree("\feedpool") sysslog("File Deletion Complete. Rebooting") sleep(5000) 'RebootSystem() else print "SD Card utilization currently: "; m.utilization endif End Sub Sub clean_getStorage() 'store value in variable for test si = createobject("roStorageInfo", "./") size% = si.GetSizeInMegabytes() used% = si.GetUsedInMegabytes() util% = used%/size%*100 'add utilitzation to diagnostic lob m.bsp.logging.WriteDiagnosticLogEntry("CardUsage", str(util%)) sysslog("Clean Plugin: Card Usage: " + str(util%)) 'percent used m.utilization = util% end Sub Sub clean_checkmyutil() 'store value in variable for test si = createobject("roStorageInfo", "./") size% = si.GetSizeInMegabytes() used% = si.GetUsedInMegabytes() util% = used%/size%*100 m.utilization = util% end Sub Sub clean_deltree(path$ as string) m.checkmyutil() sysslog(str(m.utilization)) if m.utilization >= m.utilmin then print "delete tree: "; path$ 'e.g. \pool mylistb = listdir(path$) For each item in mylistb if instr(1, item, ".") > 0 then DeleteFile(path$+"/"+item) sysslog("deleting sha file with dot: " +path$+"/"+item) else if len(item) > 1 then sysslog("deleting sha file: " +path$+"/"+item) DeleteFile(path$+"/"+item) 'folders only 1 digit long outside root else sysslog("found sub folder: " + path$+"/"+item) m.deltree(path$+"/"+item) endif endif next endif end Sub Sub onedelcache(path$ as string) listoffeeds = matchfiles(path$, "del.txt") if listoffeeds.count() = 0 then print "delete cache files: "; path$ 'e.g. \feed_cache mylistb = listdir(path$) For each item in mylistb if instr(1, item, ".") > 0 then DeleteFile(path$+"\"+item) if len(item) > 1 then DeleteFile(path$+"\"+item) 'folders only 1 digit long outside root else onedeltree(path$+"\"+item) endif next mydf = CreateObject("roCreateFile", path$+"\"+"del.txt") if mydf <> invalid then mydf.flush() endif end Sub Sub sysslog(message$ as string) slog = createobject("roSystemLog") slog.sendline(message$) end sub sub clean_settimer() 'assume clean time is midnight m.systemTime = CreateObject("roSystemTime") m.mytimer = CreateObject("roTimer") m.newTimeout = m.systemTime.GetLocalDateTime() chour = m.newtimeout.gethour() if chour < 23 then '23 sysslog("setting clean timer now") m.newTimeout.Sethour(23) 'm.cleantime% m.newTimeout.Setminute(1) '1 else m.newTimeout.AddSeconds(3600) 'check again in 1hr 3600 endif m.mytimer.SetDateTime(m.newTimeout) m.mytimer.SetPort(m.msgPort) m.mytimer.Start() end sub