Library "autoplugins.brs" 'region Main Sub Main() autorunVersion$ = "8.0.16" 'BA 5.0.1.6 customAutorunVersion$ = "8.0.16" baVersion$ = "5.0.1.6" 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.baVersion$ = baVersion$ sysInfo.deviceUniqueID$ = modelObject.GetDeviceUniqueId() sysInfo.deviceFWVersion$ = modelObject.GetVersion() sysInfo.deviceFWVersionNumber% = modelObject.GetVersionNumber() 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 videoMode = CreateObject("roVideoMode") edid = videoMode.GetEdidIdentity(true) UpdateEdidValues(edid, sysInfo) edid = invalid videoMode = invalid ' determine if the storage device is writable du = CreateObject("roStorageInfo", "./") if du.IsReadOnly() then sysInfo.storageIsWriteProtected = true else sysInfo.storageIsWriteProtected = false endif systemTime = CreateObject("roSystemTime") InitRemoteSnapshots(sysInfo) ' check to see whether or not the current firmware meets the minimum compatibility requirements versionNumber% = modelObject.GetVersionNumber() if sysInfo.deviceFamily$ = "pantera" then minVersionNumber% = 393761 minVersion$ = "6.2.33" else if sysInfo.deviceFamily$ = "pagani" then minVersionNumber% = 459275 minVersion$ = "7.2.11" else if sysInfo.deviceFamily$ = "impala" then minVersionNumber% = 393761 minVersion$ = "6.2.33" else if sysInfo.deviceFamily$ = "malibu" then minVersionNumber% = 459041 minVersion$ = "7.1.33" else if sysInfo.deviceFamily$ = "panther" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else if sysInfo.deviceFamily$ = "cheetah" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else if sysInfo.deviceFamily$ = "tiger" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else if sysInfo.deviceFamily$ = "puma" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else if sysInfo.deviceFamily$ = "bobcat" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else if sysInfo.deviceFamily$ = "lynx" then minVersionNumber% = 327952 minVersion$ = "5.1.16" else ' no supported devices should hit this 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() globalAA = GetGlobalAA() if globalAA.enableRemoteSnapshot then sleep(1000) ' sleep here ensures that graphics makes it to the screen before the snapshot is taken TakeSnapshot(systemTime, "") endif sleep(120000) RebootSystem() endif diagnosticCodes = newDiagnosticCodes() RunBsp(sysFlags, sysInfo, diagnosticCodes, systemTime) End Sub Sub InitRemoteSnapshots(sysInfo As Object) ok = CreateDirectory("snapshots") ' fails if storage is write protected or formatted as ntfs, but ignore return value at this point ' setup snapshot capability as early as possible registrySection = CreateObject("roRegistrySection", "networking") if type(registrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop globalAA = GetGlobalAA() globalAA.enableRemoteSnapshot = false if lcase(registrySection.Read("enableRemoteSnapshot")) = "yes" then if sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() globalAA.enableRemoteSnapshot = true globalAA.remoteSnapshotInterval = int(val(registrySection.Read("remoteSnapshotInterval"))) globalAA.remoteSnapshotMaxImages = int(val(registrySection.Read("remoteSnapshotMaxImages"))) globalAA.remoteSnapshotJpegQualityLevel = int(val(registrySection.Read("remoteSnapshotJpegQualityLevel"))) globalAA.remoteSnapshotDisplayPortrait = GetBoolFromString("remoteSnapshotDisplayPortrait", false) endif registrySection = invalid ' generate list of snapshots currently on card globalAA.listOfSnapshotFiles = MatchFiles("/snapshots/", "*.jpg") BubbleSortFileNames(globalAA.listOfSnapshotFiles) End Sub ' sort in ascending order from oldest to newest Sub BubbleSortFileNames(fileNames As Object) if type(fileNames) = "roList" then n = fileNames.Count() while n <> 0 newn = 0 for i = 1 to (n - 1) if fileNames[i-1] > fileNames[i] then k = fileNames[i] fileNames[i] = fileNames[i-1] fileNames[i-1] = k newn = i endif next n = newn end while endif 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() if type(globalAA.bsp) = "roAssociativeArray" then if type(globalAA.bsp.msgPort) = "roMessagePort" then globalAA.bsp.msgPort.SetWatchdogTimeout(0) endif if globalAA.enableRemoteSnapshot then sleep(1000) ' sleep here ensures that graphics makes it to the screen before the snapshot is taken TakeSnapshot(globalAA.bsp.systemTime, globalAA.bsp.activePresentation$) endif endif 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 DisplayObsoleteModelMessage(bsp As Object, publishedModel$ As String) DisplayErrorScreen("This presentation targets a BrightSign " + publishedModel$ + " and is not supported by this version of the script.", "Remove the card, publish with a version of BrightAuthor that supports this model, 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") or syncSpec.ReadFromFile("localSetupToStandalone-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.variableLoggingEnabled = m.registrySection.Read("vle") 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") m.registrySettings.networkHosts$ = m.registrySection.Read("bph") else m.registrySettings.proxy$ = "" m.registrySettings.networkHosts$ = "" 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.wirelessNetworkingParameters.enableWPAEnterpriseAuthentication = GetBoolFromString(m.registrySection.Read("enableWPAEnterpriseAuthentication"), false) m.registrySettings.wirelessNetworkingParameters.enableWPAEnterpriseAuthentication2 = GetBoolFromString(m.registrySection.Read("enableWPAEnterpriseAuthentication2"), false) 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") m.registrySettings.brightWallName$ = m.registrySection.Read("brightWallName") m.registrySettings.brightWallScreenNumber$ = m.registrySection.Read("brightWallScreenNumber") m.registrySettings.lastBSNConnectionTime = m.registrySection.Read("lastBSNConnectionTime") 'Read persistent beacon data m.registrySettings.beacon1 = m.registrySection.Read("beacon1") m.registrySettings.beacon2 = m.registrySection.Read("beacon2") End Sub Sub RunBsp(sysFlags As Object, sysInfo As Object, diagnosticCodes As Object, systemTime As Object) msgPort = CreateObject("roMessagePort") msgPort.SetWatchdogTimeout(60) BSP = newBSP(sysFlags, msgPort, systemTime) BSP.GetRegistrySettingValue = GetRegistrySettingValue BSP.ReadCachedRegistrySettings = ReadCachedRegistrySettings BSP.WriteRegistrySetting = WriteRegistrySetting BSP.ReadCachedRegistrySettings() BSP.globalVariables = NewGlobalVariables() BSP.svcPort = CreateObject("roControlPort", "BrightSign") BSP.svcPort.SetUserData("BrightSign") BSP.svcPort.SetPort(msgPort) BSP.svcPortIdentity = stri(BSP.svcPort.GetIdentity()) di = CreateObject("roDeviceInfo") if di.HasFeature("GPIO Connector") then BSP.controlPort = BSP.svcPort BSP.controlPortIdentity = BSP.svcPortIdentity else BSP.controlPort = CreateObject("roControlPort", "Expander-0-GPIO") if IsControlPort(BSP.controlPort) then BSP.controlPort.SetUserData("Expander-0-GPIO") BSP.controlPortIdentity = stri(BSP.controlPort.GetIdentity()) BSP.controlPort.SetPort(msgPort) else BSP.controlPort = {} BSP.controlPortIdentity = "" endif endif BSP.sh = CreateObject("roStorageHotplug") BSP.sh.SetPort(msgPort) BSP.nh = CreateObject("roNetworkHotplug") BSP.nh.SetPort(msgPort) BSP.diskMonitor = CreateObject("roDiskMonitor") BSP.diskMonitor.SetPort(msgPort) 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].SetUserData("LightController-0-DIAGNOSTICS") BSP.blcDiagnostics[0].SetPort(msgPort) endif BSP.blcDiagnostics[1] = CreateObject("roControlPort", "LightController-1-DIAGNOSTICS") if type(BSP.blcDiagnostics[1]) = "roControlPort" then BSP.blcDiagnostics[1].SetUserData("LightController-1-DIAGNOSTICS") BSP.blcDiagnostics[1].SetPort(msgPort) endif BSP.blcDiagnostics[2] = CreateObject("roControlPort", "LightController-2-DIAGNOSTICS") if type(BSP.blcDiagnostics[2]) = "roControlPort" then BSP.blcDiagnostics[2].SetUserData("LightController-2-DIAGNOSTICS") BSP.blcDiagnostics[2].SetPort(msgPort) endif ' create objects for all attached button panels BSP.bpInputPorts = CreateObject("roArray", 4, true) BSP.bpInputPortIdentities = CreateObject("roArray", 4, true) BSP.bpInputPortHardware = CreateObject("roArray", 4, true) BSP.bpInputPortConfigurations = CreateObject("roArray", 4, true) BSP.bpInputPorts[0] = CreateObject("roControlPort", "TouchBoard-0-GPIO") if type(BSP.bpInputPorts[0]) = "roControlPort" then BSP.bpInputPorts[0].SetUserData("TouchBoard-0-GPIO") 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.bpInputPorts[1].SetUserData("TouchBoard-1-GPIO") 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.bpInputPorts[2].SetUserData("TouchBoard-2-GPIO") 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.bpInputPorts[3] = CreateObject("roControlPort", "TouchBoard-3-GPIO") if type(BSP.bpInputPorts[3]) = "roControlPort" then BSP.bpInputPorts[3].SetUserData("TouchBoard-3-GPIO") BSP.bpInputPortIdentities[3] = stri(BSP.bpInputPorts[3].GetIdentity()) BSP.bpInputPorts[3].SetPort(msgPort) properties = BSP.bpInputPorts[3].GetProperties() BSP.bpInputPortHardware[3] = properties.hardware BSP.bpInputPortConfigurations[3] = 0 endif BSP.sysInfo = sysInfo BSP.diagnosticCodes = diagnosticCodes BSP.diagnostics.SetSystemInfo(sysInfo, diagnosticCodes) BSP.logging.SetSystemInfo(sysInfo, diagnosticCodes) ' Create device specific User-Agent string BSP.userAgent$ = "BrightSign/" + sysInfo.deviceUniqueID$ + "/" + sysInfo.deviceFWVersion$ + " (" + sysInfo.deviceModel$ + ")" ' 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() lwsEnabled = false if BSP.registrySettings.lwsConfig$ = "c" or BSP.registrySettings.lwsConfig$ = "s" then lwsEnabled = true endif if lwsEnabled then BSP.localServer = CreateObject("roHttpServer", { port: 8080 }) BSP.localServer.SetPort(msgPort) if lwsEnabled then lwsUserName$ = BSP.registrySettings.lwsUserName$ lwsPassword$ = BSP.registrySettings.lwsPassword$ if (len(lwsUserName$) + len(lwsPassword$)) > 0 then lwsCredentials = CreateObject("roAssociativeArray") lwsCredentials.AddReplace(lwsUserName$, lwsPassword$) else lwsCredentials = invalid end if endif 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.GetSnapshotConfigurationAA = { HandleEvent: GetSnapshotConfiguration, mVar: BSP } BSP.SetSnapshotConfigurationAA = { HandleEvent: SetSnapshotConfiguration, mVar: BSP } BSP.GetSnapshotAA = { HandleEvent: GetSnapshot, mVar: BSP } BSP.GetBSNStatusAA = { HandleEvent: GetBSNStatus, mVar: BSP } BSP.SetBSNOverrideAA = { HandleEvent: SetBSNOverride, mVar: BSP } BSP.CancelBSNOverrideAA = { HandleEvent: CancelBSNOverride, 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: "/", 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.AddGetFromEvent({ url_path: "/GetCurrentStatus", user_data: BSP.GetCurrentStatusAA, passwords: lwsCredentials }) BSP.localServer.AddPostToFile({ url_path: "/UploadFile", destination_directory: GetDefaultDrive(), user_data: BSP.FilePostedAA, passwords: lwsCredentials }) BSP.localServer.AddPostToFile({ url_path: "/UploadSyncSpec", destination_directory: GetDefaultDrive(), user_data: BSP.SyncSpecPostedAA, passwords: lwsCredentials }) BSP.localServer.AddPostToFile({ url_path: "/PrepareForTransfer", destination_directory: GetDefaultDrive(), user_data: BSP.PrepareForTransferAA, passwords: lwsCredentials }) BSP.localServer.AddGetFromEvent({ url_path: "/SpecifyCardSizeLimits", user_data: BSP.SpecifyCardSizeLimitsAA, passwords: lwsCredentials}) BSP.localServer.AddGetFromEvent({ url_path: "/GetSnapshotConfiguration", user_data: BSP.GetSnapshotConfigurationAA}) BSP.localServer.AddPostToFormData({ url_path: "/SetSnapshotConfiguration", user_data: BSP.SetSnapshotConfigurationAA, passwords: lwsCredentials}) BSP.localServer.AddGetFromEvent({ url_path: "/GetSnapshot", user_data: BSP.GetSnapshotAA, passwords: lwsCredentials}) BSP.localServer.AddGetFromEvent({ url_path: "/bsn/status", user_data: BSP.GetBSNStatusAA, passwords: lwsCredentials}) BSP.localServer.AddPostToFormData({ url_path: "/bsn/override", user_data: BSP.SetBSNOverrideAA, passwords: lwsCredentials}) BSP.localServer.AddGetFromEvent({ url_path: "/bsn/override", user_data: BSP.CancelBSNOverrideAA, passwords: lwsCredentials}) ' BSP.localServer.AddMethodFromEvent({ method: "DELETE", url_path: "/bsn/override", user_data: BSP.CancelBSNOverrideAA, passwords: lwsCredentials}) 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 NTFS ok = CreateDirectory("pool") ok = CreateDirectory("feedPool") ok = CreateDirectory("feed_cache") ok = CreateDirectory("htmlWidgets") ' ok = CreateDirectory("snapshots") BSP.assetPool = CreateObject("roAssetPool", "pool") BSP.feedPool = CreateObject("roAssetPool", "feedPool") activeSyncSpec = invalid BSP.contentEncrypted = false localCurrentSync = CreateObject("roSyncSpec") if localCurrentSync.ReadFromFile("local-sync.xml") or localCurrentSync.ReadFromFile("localSetupToStandalone-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 BSP.GetSupportedFeatures() if metadata.DoesExist("obfuscatedPassphrase") and BSP.contentEncryptionSupported then obfuscatedPassphrase$ = localCurrentSync.LookupMetadata("client", "obfuscatedPassphrase") deviceCustomization = CreateObject("roDeviceCustomization") deviceCustomization.StoreObfuscatedEncryptionKey("AesCtrHmac", obfuscatedPassphrase$) BSP.contentEncrypted = true 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.GetSupportedFeatures() deviceCustomization = CreateObject("roDeviceCustomization") if BSP.contentEncryptionSupported and deviceCustomization.IsEncryptionKeyPresent("AesCtrHmac") then BSP.contentEncrypted = true endif BSP.SetPerFileEncryptionStatus(networkedCurrentSync) 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 variableLoggingEnabled = 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", "variableLoggingEnabled") = "yes" then variableLoggingEnabled = 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 if localCurrentSync.LookupMetadata("client", "variableLoggingEnabled") = "yes" then variableLoggingEnabled = true else if localCurrentSync.LookupMetadata("client", "variableLoggingEnabled") = "no" then variableLoggingEnabled = false else if BSP.registrySettings.variableLoggingEnabled = "yes" then variableLoggingEnabled = 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.variableLoggingEnabled = "yes" then variableLoggingEnabled = 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 or variableLoggingEnabled) and BSP.sysInfo.storageIsWriteProtected then DisplayStorageDeviceLockedMessage() BSP.variablesDBExists = false ' setup logging BSP.logging.InitializeLogging(playbackLoggingEnabled, eventLoggingEnabled, stateLoggingEnabled, diagnosticLoggingEnabled, variableLoggingEnabled, 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, "AssetPool Protect Failure") BSP.logging.FlushLogFile() BSP.diagnostics.PrintDebug("### ProtectFiles failed: " + "AssetPool Protect Failure") stop endif ' limit pool sizes BSP.limitStorageSpace = false BSP.SetPoolSizes( activeSyncSpec ) ' setup BrightSign application server if lwsEnabled then BSP.localServer.SetupDWSLink("Application") ' retrieve the files that are part of the BrightSign application server prefix$ = "BrightSignApplicationServer-" allFiles = activeSyncSpec.GetFileList("download") for each fileNameAA in allFiles syncSpecFileName$ = fileNameAA.name if instr(1, syncSpecFileName$, prefix$) = 1 then fileName$ = mid(syncSpecFileName$, len(prefix$) + 1) ext = GetFileExtension(fileName$) if ext <> invalid then contentType$ = GetMimeTypeByExtension(ext) if contentType$ <> invalid then url$ = "/" + fileName$ filePath$ = GetPoolFilePath(BSP.assetPoolFiles, syncSpecFileName$) BSP.localServer.AddGetFromFile({ url_path: url$, filename: filePath$, content_type: contentType$, passwords: lwsCredentials }) if fileName$ = "index.html" then BSP.localServer.AddGetFromFile({ url_path: "/", filename: filePath$, content_type: contentType$, passwords: lwsCredentials }) endif endif endif endif next endif ' Read and parse BoseProducts.xml BSP.boseProductSpecs = ReadBoseProductsFile() ' Get tuner data BSP.scannedChannels = GetScannedChannels() BSP.tunerScanPercentageComplete$ = "0" ' Set up Bluetooth manager for players that support Bluetooth BSP.btManager = BSP.newBtManager() ' 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[4,11] dim bpInputUsed[4,11] dim bpOutputUsed[4,11] dim bpSM[4,11] BSP.bpStateMachineRequired = bpStateMachineRequired BSP.bpInputUsed = bpInputUsed BSP.bpOutputUsed = bpOutputUsed BSP.bpSM = bpSM ' 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 Function IsControlPort(controlPort As Object) As Boolean return type(controlPort) = "roControlPort" End Function Sub GetSupportedFeatures() featureMinRevsFilePath$ = GetPoolFilePath(m.assetPoolFiles, "featureMinRevs.xml") m.featureMinRevs = ParseFeatureMinRevs(featureMinRevsFilePath$) ' set booleans for each feature where autorun needs to know whether this version of FW supports it m.useInitiatorAddressFromPacketSupported = IsFeatureSupported("UseInitiatorAddressFromPacket", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.setSyncDomainSupported = IsFeatureSupported("SetSyncDomain", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.setDNSServersSupported = IsFeatureSupported("ResetInterfaceSettings", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.forceResolutionSupported = IsFeatureSupported("ForceResolution", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.showHideVideoZoneSupported = IsFeatureSupported("ShowHideVideoZone", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.bypassProxySupported = IsFeatureSupported("BypassProxy", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.contentEncryptionSupported = IsFeatureSupported("ContentEncryption", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.htmlSetTransformSupported = IsFeatureSupported("HtmlSetTransform", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.httpWidgetGetUserAgentSupported = IsFeatureSupported("HttpWidgetGetUserAgent", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.mosaicModeSupported = IsFeatureSupported("MosaicMode", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.beaconsSupported = IsFeatureSupported("Beacons", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.btleSupported = IsFeatureSupported("Btle", m.sysInfo.deviceFWVersion$, m.featureMinRevs) m.fullResolutionGraphicsSupported = IsFeatureSupported("FullResolutionGraphics", m.sysInfo.deviceFWVersion$, m.featureMinRevs) End Sub Sub SetPerFileEncryptionStatus(syncSpec As Object) ' get per file encryption status m.encryptionByFile = {} listOfDownloads = syncSpec.GetFileList("download") for each download in listOfDownloads if download.DoesExist("encryption") then m.encryptionByFile.AddReplace(download.name, download.encryption) endif next End Sub Function ParseFeatureMinRevs(path$ As String) featureMinRevs = {} featureMinRevs$ = ReadAsciiFile(path$) if len(featureMinRevs$) > 0 then featureMinRevsXML = CreateObject("roXMLElement") featureMinRevsXML.Parse(featureMinRevs$) ' verify that this is a valid FeatureMinRevs XML file if featureMinRevsXML.GetName() <> "FeatureMinRevs" then print "Invalid FeatureMinRevs XML file - name not FeatureMinRevs" : stop if not IsString(featureMinRevsXML@version) then print "Invalid FeatureMinRevs XML file - version not found" : stop featuresXML = featureMinRevsXML.feature for each featureXML in featuresXML featureName$ = featureXML.name.GetText() minRev$ = featureXML.minFWRev.GetText() featureMinRevs.AddReplace(featureName$, minRev$) next endif return featureMinRevs End Function 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") htmlLocalStorageSizeLimitPercentage = syncSpec.LookupMetadata("client", "htmlLocalStorageSizeLimitPercentage") htmlLocalStorageSizeLimitMB = syncSpec.LookupMetadata("client", "htmlLocalStorageSizeLimitMB") htmlIndexedDBSizeLimitPercentage = syncSpec.LookupMetadata("client", "htmlIndexedDBSizeLimitPercentage") htmlIndexedDBSizeLimitMB = syncSpec.LookupMetadata("client", "htmlIndexedDBSizeLimitMB") 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 ' size for html local storage htmlLocalStorageSizeLimitPercentage% = 0 maximumHTMLLocalStoragePoolSizeMB% = 0 if htmlLocalStorageSizeLimitPercentage <> "" then htmlLocalStorageSizeLimitPercentage% = int(val(htmlLocalStorageSizeLimitPercentage)) maximumHTMLLocalStoragePoolSizeMB% = htmlLocalStorageSizeLimitPercentage% * totalCardSizeMB% / 100 endif ' size for html indexed db htmlIndexedDBSizeLimitPercentage% = 0 maximumHTMLIndexedDBPoolSizeMB% = 0 if htmlIndexedDBSizeLimitPercentage <> "" then htmlIndexedDBSizeLimitPercentage% = int(val(htmlIndexedDBSizeLimitPercentage)) maximumHTMLIndexedDBPoolSizeMB% = htmlIndexedDBSizeLimitPercentage% * totalCardSizeMB% / 100 endif else maximumPublishedDataPoolSizeMB% = int(val(publishedDataSizeLimitMB)) maximumDynamicDataPoolSizeMB% = int(val(dynamicDataSizeLimitMB)) maximumHTMLDataPoolSizeMB% = int(val(htmlDataSizeLimitMB)) maximumHTMLLocalStoragePoolSizeMB% = 0 if htmlLocalStorageSizeLimitMB <> "" then maximumHTMLLocalStoragePoolSizeMB% = int(val(htmlLocalStorageSizeLimitMB)) endif maximumHTMLIndexedDBPoolSizeMB% = 0 if htmlIndexedDBSizeLimitMB <> "" then maximumHTMLIndexedDBPoolSizeMB% = int(val(htmlIndexedDBSizeLimitMB)) endif 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("/HTMLAppCache") htmlWidget = invalid endif endif if maximumHTMLLocalStoragePoolSizeMB% > 0 then ' max int is 2147483647 => <2048 MB if maximumHTMLLocalStoragePoolSizeMB% >= 2048 then maximumHTMLLocalStoragePoolSizeMB% = 2047 endif r=CreateObject("roRectangle",0,0,0,0) htmlWidget = CreateObject("roHtmlWidget", r) if type(htmlWidget) = "roHtmlWidget" then ok = htmlWidget.SetLocalStorageDir("localstorage") ok = htmlWidget.SetLocalStorageQuota(maximumHTMLLocalStoragePoolSizeMB% * 1024 * 1024) htmlWidget = invalid endif endif if maximumHTMLIndexedDBPoolSizeMB% > 0 then ' max int is 2147483647 => <2048 MB if maximumHTMLIndexedDBPoolSizeMB% >= 2048 then maximumHTMLIndexedDBPoolSizeMB% = 2047 endif r=CreateObject("roRectangle",0,0,0,0) htmlWidget = CreateObject("roHtmlWidget", r) if type(htmlWidget) = "roHtmlWidget" then ok = htmlWidget.SetWebDatabaseDir("IndexedDB") ok = htmlWidget.SetWebDatabaseQuota(maximumHTMLIndexedDBPoolSizeMB% * 1024 * 1024) 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 Sub DiagnoseAndRecoverWifiNetwork(event As Object) ' Check if event is url event if type(event) <> "roUrlEvent" then return ' Return if had tried create wifi interface and failed if m.createWifiFailure = true then return ' Create wifi interface if not exist if m.wifiNetworkConfiguration = invalid then m.wifiNetworkConfiguration = CreateObject("roNetworkConfiguration", 1) endif ' Return if failed to create wifi interface if type(m.wifiNetworkConfiguration) <> "roNetworkConfiguration" then m.createWifiFailure = true return endif ' Check if OS has function to safely reassociate wifi if not IsBoolean(m.osSupportsReassociateWifi) then m.osSupportsReassociateWifi = (findMemberFunction(m.wifiNetworkConfiguration, "ReassociateWiFi") <> invalid) endif ' Return if OS doesn't support reassociate wifi if m.osSupportsReassociateWifi <> true then return if not IsBoolean(m.disableWifiAutoRecovery) then ' GetGlobalAA().registrySection is the "networking" registry section ' NOTE: If this registry key is changed, it will not take effect until autorun is restarted. registryKey = GetGlobalAA().bsp.registrySection.Read("disable_wifi_auto_recovery") m.disableWifiAutoRecovery = GetBoolFromString(registryKey, false) endif ' Return if disabled by registry key if m.disableWifiAutoRecovery = true then return essid = m.wifiNetworkConfiguration.GetWiFiESSID() if essid = invalid then essid = "" ' Exit if wifi not configured if essid = "" then return responseCode = event.GetResponseCode() if responseCode >= 0 then ' Clear failure count and return if there is a successful connection m.minusSixFailureCount = 0 m.minusTwentyEightFailureCount = 0 m.minusFiftySixFailureCount = 0 return endif ' -6 is CURLE_COULDNT_RESOLVE_HOST if responseCode = -6 then m.minusSixFailureCount = m.minusSixFailureCount + 1 endif ' -28 is CURLE_OPERATION_TIMEDOUT if responseCode = -28 then m.minusTwentyEightFailureCount = m.minusTwentyEightFailureCount + 1 endif ' -56 is CURLE_RECV_ERROR if responseCode = -56 then m.minusFiftySixFailureCount = m.minusFiftySixFailureCount + 1 endif ' Make sure at least 5 mins(300 in seconds) has passed since last network reset ' to leave some time for the network re-connection RECONNECT_SECONDS = 300 if (m.lastWifiReconnectTimestampInSeconds <> 0) and (m.systemTime.GetLocalDateTime().ToSecondsSinceEpoch() - m.lastWifiReconnectTimestampInSeconds <= RECONNECT_SECONDS) then return endif ' Check failure count against kick-wifi threshold RECONNECT_LIMIT = 5 kick = false if (m.minusSixFailureCount > RECONNECT_LIMIT) or (m.minusTwentyEightFailureCount > RECONNECT_LIMIT) or (m.minusFiftySixFailureCount > RECONNECT_LIMIT) then kick = true endif currentConfig = m.wifiNetworkConfiguration.GetCurrentConfig() ip4_address = currentConfig.ip4_address ipInvalid = (ip4_address = Invalid or ip4_address = "" or instr(1, ip4_address, "169.254") = 1) ' If player has a link, but ip address is invalid, that is a sign ' that network gets stuck. Will try reconnect immediately. ' Otherwise will wait for the failure count met the threshold isStuck = (currentConfig.link = true) and ipInvalid ' Check and test for IP address if (isStuck or kick) then error_rates$ = "-6_count=" + stri(m.minusSixFailureCount) + ", -28_count=" + stri(m.minusTwentyEightFailureCount) + ", -56_count=" + stri(m.minusFiftySixFailureCount) if ip4_address = invalid then ip4_address = "" m.PrintDebug("Recovering wifi: interface=" + essid + ", ip4_address=" + ip4_address) m.PrintDebug("Error counts: " + error_rates$) m.wifiNetworkConfiguration.ReassociateWiFi() m.lastWifiReconnectTimestampInSeconds = m.systemTime.GetLocalDateTime().ToSecondsSinceEpoch() ' We have attempt to reconnect, clear failure counts m.minusSixFailureCount = 0 m.minusTwentyEightFailureCount = 0 m.minusFiftySixFailureCount = 0 endif End Sub Function newBSP(sysFlags As Object, msgPort As Object, systemTime As Object) As Object BSP = {} globalAA = GetGlobalAA() globalAA.bsp = BSP BSP.globalAA = globalAA BSP.msgPort = msgPort registrySection = CreateObject("roRegistrySection", "networking") if type(registrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop BSP.registrySection = registrySection BSP.systemTime = systemTime 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.SetUSBAudioOutput = SetUSBAudioOutput 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.CecSetSourceBrightSign = CecSetSourceBrightSign 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.ScheduleRetryCreateSerial = ScheduleRetryCreateSerial BSP.RetryCreateSerial = RetryCreateSerial BSP.AttemptOpenSerial = AttemptOpenSerial 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.DBIsValid = DBIsValid BSP.DBTablesExist = DBTablesExist 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.GetDBTableNames = GetDBTableNames 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.GetDBTableNamesCallback = GetDBTableNamesCallback 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.AssignSystemVariablesToUserVariables = AssignSystemVariablesToUserVariables BSP.AssignSystemVariableToUserVariables = AssignSystemVariableToUserVariables 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 BSP.InitiateRemoteSnapshotTimer = InitiateRemoteSnapshotTimer BSP.RemoveRemoteSnapshotTimer = RemoveRemoteSnapshotTimer BSP.NetworkingIsActive = NetworkingIsActive BSP.DeviceSupportsRotation = DeviceSupportsRotation BSP.GetSupportedFeatures = GetSupportedFeatures BSP.encryptionByFile = {} BSP.SetPerFileEncryptionStatus = SetPerFileEncryptionStatus BSP.SetEncryptionAttributes = SetEncryptionAttributes BSP.QueueRetrieveLiveDataFeed = QueueRetrieveLiveDataFeed BSP.RetrieveLiveDataFeed = RetrieveLiveDataFeed BSP.RetrievePendingLiveDataFeed = RetrievePendingLiveDataFeed BSP.AdvanceToNextLiveDataFeedInQueue = AdvanceToNextLiveDataFeedInQueue BSP.RemoveFailedFeedFromQueue = RemoveFailedFeedFromQueue BSP.newBtManager = newBtManager 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$) elem = root.AddElement("bsnActive") if mVar.NetworkingIsActive() then elem.SetBody("yes") else elem.SetBody("no") endif elem = root.AddElement("snapshotsAvailable") globalAA = GetGlobalAA() if globalAA.listOfSnapshotFiles.Count() > 0 then elem.SetBody("yes") else elem.SetBody("no") endif 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") elem = root.AddElement("activePresentation") if mVar.activePresentation$ <> invalid then elem.SetBody(mVar.activePresentation$) endif 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 root = CreateObject("roXMLElement") root.SetName("BrightSignUserVariables") PopulateUserVarData(mVar, root) xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml; charset=utf-8") e.SetResponseBodyString(xml) e.SendResponse(200) End Sub Sub GetUserVariableCategories(userData as Object, e as Object) mVar = userData.mVar 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; charset=utf-8") 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 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; charset=utf-8") 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$) if mVar.globalAA.enableRemoteSnapshot then numSnapshots% = mVar.globalAA.listOfSnapshotFiles.Count() if numSnapshots% > 0 then latestSnapshot = mVar.globalAA.listOfSnapshotFiles[numSnapshots%-1] index% = instr(1, latestSnapshot, ".jpg") if index% > 0 then id$ = mid(latestSnapshot, 1, index% - 1) elem = root.AddElement("snapshotId") elem.AddAttribute("label", "Snapshot ID") elem.SetBody(id$) elem = root.AddElement("snapshotDisplayPortrait") elem.AddAttribute("label", "Snapshot Display Portrait") if mVar.globalAA.remoteSnapshotDisplayPortrait then elem.SetBody("true") else elem.SetBody("false") endif endif endif endif ' 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 Sub PopulateSnapshotData(mVar As Object, root As Object, startTimeSpecified As Boolean, startTime As Object) globalAA = GetGlobalAA() for each snapshotFile in globalAA.listOfSnapshotFiles ' get timestamp, id from file name index% = instr(1, snapshotFile, ".jpg") if index% > 0 then time$ = mid(snapshotFile, 1, index% - 1) snapshotTime = CreateObject("roDateTime") snapshotTime.FromIsoString(time$) if not startTimeSpecified or snapshotTime.GetString() >= startTime.GetString() then itemElem = root.AddElement("Item") timeElem = itemElem.AddElement("Time") timeElem.SetBody(time$) idElem = itemElem.AddElement("ID") idElem.SetBody(time$) ' imagePathElem = itemElem.AddElement("ImagePath") ' imagePathElem.SetBody("/snapshots/" + snapshotFile) endif endif next End Sub Function GetSnapshotConfiguration(userData as Object, e as Object) As Object mVar = userData.mVar globalAA = GetGlobalAA() root = CreateObject("roXMLElement") root.SetName("BrightSignSnapshots") root.AddAttribute("Count", StripLeadingSpaces(stri(globalAA.listOfSnapshotFiles.Count()))) if globalAA.enableRemoteSnapshot <> invalid then if globalAA.enableRemoteSnapshot then strVal = "true" else strVal = "false" end if root.AddAttribute("Enabled", strVal) endif if globalAA.remoteSnapshotInterval <> invalid then root.AddAttribute("Interval", StripLeadingSpaces(stri(globalAA.remoteSnapshotInterval))) endif if globalAA.remoteSnapshotMaxImages <> invalid then root.AddAttribute("MaxImages", StripLeadingSpaces(stri(globalAA.remoteSnapshotMaxImages))) endif if globalAA.remoteSnapshotJpegQualityLevel <> invalid then root.AddAttribute("Quality", StripLeadingSpaces(stri(globalAA.remoteSnapshotJpegQualityLevel))) endif if mVar.videoMode <> invalid then root.AddAttribute("ResX", StripLeadingSpaces(stri(mVar.videoMode.GetOutputResX()))) root.AddAttribute("ResY", StripLeadingSpaces(stri(mVar.videoMode.GetOutputResY()))) endif if globalAA.remoteSnapshotDisplayPortrait <> invalid then remoteSnapshotDisplayPortrait = "false" if globalAA.remoteSnapshotDisplayPortrait then remoteSnapshotDisplayPortrait = "true" endif root.AddAttribute("DisplayPortraitMode", remoteSnapshotDisplayPortrait) endif ' CanConfigure attribute notifies clients if snapshot configuration can be managed by this unit root.AddAttribute("CanConfigure","true") startTimeSpecified = false startDT = CreateObject("roDateTime") startTime$ = e.GetRequestParam("starttime") if startTime$ <> "" then startTimeSpecified = startDT.FromIsoString(startTime$ + ".000") endif PopulateSnapshotData(mVar, root, startTimeSpecified, startDT) xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Function Function SetSnapshotConfiguration(userData as Object, e as Object) As Object mVar = userData.mVar ' only set the override timer if appropriate (networking configured / active) overrideModeActive = false if mVar.NetworkingIsActive() then args = e.GetFormData() if type(args) = "roAssociativeArray" and type(args.remoteSnapshotBsnTimeout) = "roString" then timeout% = int(val(args.remoteSnapshotBsnTimeout)) mVar.overrideBSNTimer = CreateObject("roTimer") mVar.overrideBSNTimer.SetPort(mvar.msgPort) mVar.overrideBSNTimer.SetElapsed(timeout%, 0) mVar.overrideBSNTimer.Start() mVar.diagnostics.PrintDebug("### SetBSNOverride for " + args.remoteSnapshotBsnTimeout + " seconds.") mVar.logging.WriteDiagnosticLogEntry(mVar.diagnosticCodes.EVENT_SET_BSN_OVERRIDE, "### SetBSNOverride for " + "69" + " seconds.") overrideModeActive = true endif endif mVar.diagnostics.PrintDebug("### SetSnapshotConfiguration") mVar.logging.WriteDiagnosticLogEntry(mVar.diagnosticCodes.EVENT_SET_SNAPSHOT_CONFIGURATION, "") e.AddResponseHeader("Content-type", "text/plain") ' don't allow the user to set the configuration is bsn is connected and not in override mode if mVar.NetworkingIsActive() and not overrideModeActive then e.SetResponseBodyString("") if not e.SendResponse(403) then stop return 0 endif globalAA = GetGlobalAA() ' remember current settings currentEnableRemoteSnapshot = globalAA.enableRemoteSnapshot currentRemoteSnapshotInterval = globalAA.remoteSnapshotInterval currentRemoteSnapshotMaxImages = globalAA.remoteSnapshotMaxImages currentRemoteSnapshotJpegQualityLevel = globalAA.remoteSnapshotJpegQualityLevel currentRemoteSnapshotDisplayPortrait = globalAA.remoteSnapshotDisplayPortrait ' set global values from form data args = e.GetFormData() if args.enableRemoteSnapshot <> invalid then globalAA.enableRemoteSnapshot = GetBoolFromString(args.enableRemoteSnapshot, false) endif if args.remoteSnapshotInterval <> invalid then globalAA.remoteSnapshotInterval = int(val(args.remoteSnapshotInterval)) endif if args.remoteSnapshotMaxImages <> invalid then globalAA.remoteSnapshotMaxImages = int(val(args.remoteSnapshotMaxImages)) endif if args.remoteSnapshotJpegQualityLevel <> invalid then globalAA.remoteSnapshotJpegQualityLevel = int(val(args.remoteSnapshotJpegQualityLevel)) endif if args.remoteSnapshotDisplayPortrait <> invalid then globalAA.remoteSnapshotDisplayPortrait = GetBoolFromString(args.remoteSnapshotDisplayPortrait, false) endif if not overrideModeActive then ' set registry values if not in override mode registrySection = CreateObject("roRegistrySection", "networking") if type(registrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop registrySection.Write("enableRemoteSnapshot", GetYesNoFromBool(globalAA.enableRemoteSnapshot)) registrySection.Write("remoteSnapshotInterval", stri(globalAA.remoteSnapshotInterval)) registrySection.Write("remoteSnapshotMaxImages", stri(globalAA.remoteSnapshotMaxImages)) registrySection.Write("remoteSnapshotJpegQualityLevel", stri(globalAA.remoteSnapshotJpegQualityLevel)) registrySection.Write("remoteSnapshotDisplayPortrait", GetYesNoFromBool(globalAA.remoteSnapshotDisplayPortrait)) endif if globalAA.enableRemoteSnapshot then DeleteExcessSnapshots(globalAA) if not currentEnableRemoteSnapshot or globalAA.remoteSnapshotInterval <> currentRemoteSnapshotInterval then mVar.InitiateRemoteSnapshotTimer() endif else if currentEnableRemoteSnapshot then mVar.RemoveRemoteSnapshotTimer() endif e.SetResponseBodyString("OK") e.SendResponse(200) End Function Function GetSnapshot(userData as Object, e as Object) As Object mVar = userData.mVar globalAA = GetGlobalAA() listOfSnapshotFiles = globalAA.listOfSnapshotFiles snapshotID$ = e.GetRequestParam("ID") if snapshotID$ <> "" then ' perform linear search to find image for each snapshotFile in listOfSnapshotFiles if snapshotFile = snapshotID$ + ".jpg" then e.AddResponseHeader("Content-type", "image/jpeg") e.SetResponseBodyFile("snapshots/" + snapshotID$ + ".jpg") e.SendResponse(200) return 0 endif next endif e.AddResponseHeader("Content-type", "text/plain; charset=utf-8") if snapshotID$ <> "" then e.SetResponseBodyString("Snapshot file corresponding to ID " + snapshotID$ + " not found") else e.SetResponseBodyString("Snapshot ID not specified.") endif e.SendResponse(404) End Function Function GetBSNStatus(userData as Object, e as Object) As Object print "respond to GetBSNStatus request" mVar = userData.mVar root = CreateObject("roXMLElement") root.SetName("BrightSignID") elem = root.AddElement("bsnActive") if mVar.NetworkingIsActive() then elem.SetBody("yes") else elem.SetBody("no") endif xml = root.GenXML({ indent: " ", newline: chr(10), header: true }) e.AddResponseHeader("Content-type", "text/xml") e.SetResponseBodyString(xml) e.SendResponse(200) End Function Function SetBSNOverride(userData as Object, e as Object) As Object print "respond to SetBSNOverride request" mVar = userData.mVar ' only set the override timer if appropriate (networking configured / active) if mVar.NetworkingIsActive() then args = e.GetFormData() timeout% = int(val(args.timeout)) mVar.overrideBSNTimer = CreateObject("roTimer") mVar.overrideBSNTimer.SetPort(mvar.msgPort) mVar.overrideBSNTimer.SetElapsed(timeout%, 0) mVar.overrideBSNTimer.Start() mVar.diagnostics.PrintDebug("### SetBSNOverride for " + args.timeout + " seconds.") mVar.logging.WriteDiagnosticLogEntry(mVar.diagnosticCodes.EVENT_SET_BSN_OVERRIDE, "### SetBSNOverride for " + args.timeout + " seconds.") endif e.AddResponseHeader("Content-type", "text/plain") e.SetResponseBodyString("OK") e.SendResponse(200) End Function Function CancelBSNOverride(userData as Object, e as Object) As Object print "respond to CancelBSNOverride request" mVar = userData.mVar mVar.diagnostics.PrintDebug("### CancelBSNOverride") mVar.logging.WriteDiagnosticLogEntry(mVar.diagnosticCodes.EVENT_CANCEL_BSN_OVERRIDE, "") if type(mVar.overrideBSNTimer) = "roTimer" then mVar.overrideBSNTimer = invalid mVar.networkingHSM.SetBASParameters(mVar.networkingHSM.currentSync, false, false, true, true) endif e.AddResponseHeader("Content-type", "text/plain") e.SetResponseBodyString("OK") e.SendResponse(200) 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")) mVar.htmlLocalStorageSizeLimitPercentage = 0 mVar.htmlLocalStorageSizeLimitMB = 0 if e.GetRequestParam("htmlDataSizeLimitPercentage") <> "" then mVar.htmlLocalStorageSizeLimitPercentage = ConvertToInt(e.GetRequestParam("htmlLocalStorageSizeLimitPercentage")) mVar.htmlLocalStorageSizeLimitMB = ConvertToInt(e.GetRequestParam("htmlLocalStorageSizeLimitMB")) endif mVar.htmlIndexedDBSizeLimitPercentage = 0 mVar.htmlIndexedDBSizeLimitMB = 0 if e.GetRequestParam("htmlIndexedDBSizeLimitPercentage") <> "" then mVar.htmlIndexedDBSizeLimitPercentage = ConvertToInt(e.GetRequestParam("htmlIndexedDBSizeLimitPercentage")) mVar.htmlIndexedDBSizeLimitMB = ConvertToInt(e.GetRequestParam("htmlIndexedDBSizeLimitMB")) endif 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") root.AddAttribute("Family", mVar.sysInfo.deviceFamily$) root.AddAttribute("Model", mVar.sysInfo.deviceModel$) root.AddAttribute("FWVersion", mVar.sysInfo.deviceFWVersion$) root.AddAttribute("FWVersionNumber", StripLeadingSpaces(stri(mVar.sysInfo.deviceFWVersionNumber%))) 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 metadata = newSync.GetMetadata("client") if metadata.DoesExist("obfuscatedPassphrase") and mVar.contentEncryptionSupported then obfuscatedPassphrase$ = newSync.LookupMetadata("client", "obfuscatedPassphrase") deviceCustomization = CreateObject("roDeviceCustomization") deviceCustomization.StoreObfuscatedEncryptionKey("AesCtrHmac", obfuscatedPassphrase$) mVar.contentEncrypted = true endif 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 newVideoModePlugin(videoModePluginXML As Object) As Object videoModePlugin = {} videoModePlugin.fileName$ = videoModePluginXML.fileName.GetText() videoModePlugin.functionName$ = videoModePluginXML.functionName.GetText() videoModePlugin.plugin = invalid return videoModePlugin End Function Function newParserPlugin(parserPluginXML As Object) As Object parserPlugin = {} parserPlugin.name$ = parserPluginXML.name.GetText() parserPlugin.parseFeedFunction$ = parserPluginXML.parseFeedFunctionName.GetText() parserPlugin.parseUVFunction$ = parserPluginXML.parseUVFunctionName.GetText() parserPlugin.userAgentFunction$ = parserPluginXML.userAgentFunctionName.GetText() return parserPlugin End Function Function getParserPlugin(bsp As Object, parserPluginName As Object) As Object if Len(parserPluginName) > 0 then for each plugin in bsp.parserPlugins if plugin.name$ = parserPluginName then return plugin endif next endif return invalid End Function Function newHTMLSite(bsp As Object, htmlSiteXML As Object) As Object htmlSite = {} htmlSite.name$ = htmlSiteXML.name.GetText() htmlSite.queryString = newParameterValue(bsp, htmlSiteXML.queryString.parameterValue) if htmlSiteXML.GetName() = "localHTMLSite" or htmlSiteXML.GetName() = "brightPlateHTMLSite" 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 Sub ParseProxyBypass(bsp, xml) bypassProxy$ = xml.bypassProxy.GetText() if bypassProxy$ <> "" then if lcase(bypassProxy$) = "true" then hostName$ = xml.hostName.GetText() if hostName$ <> "" then bsp.bypassProxyHosts.push(hostName$) endif endif endif End Sub Function newLiveDataFeed(bsp As Object, liveDataFeedXML As Object) As Object liveDataFeed = InitializeLiveDataFeedParameters( bsp ) liveDataFeed.name$ = CleanName( liveDataFeedXML.name.GetText() ) liveBSNDataFeedXMLList = liveDataFeedXML.GetNamedElements("liveBSNDataFeed") liveBSNMediaFeedXMLList = liveDataFeedXML.GetNamedElements("liveBSNMediaFeed") liveDynamicPlaylistXMLList = liveDataFeedXML.GetNamedElements("liveDynamicPlaylist") liveBSNTaggedPlaylistFeedXMLList = liveDataFeedXML.GetNamedElements("liveBSNTaggedPlaylist") liveDataFeed.isDynamicPlaylist = false liveDataFeed.isLiveMediaFeed = false if liveBSNDataFeedXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveBSNDataFeed.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) else if liveBSNMediaFeedXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveBSNMediaFeed.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) liveDataFeed.isLiveMediaFeed = true else if liveDynamicPlaylistXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveDynamicPlaylist.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) liveDataFeed.isDynamicPlaylist = true else if liveBSNTaggedPlaylistFeedXMLList.Count() = 1 then urlSpec$ = liveDataFeedXML.liveBSNTaggedPlaylist.url.GetText() liveDataFeed.url = newTextParameterValue(urlSpec$) ' Set isLiveMediaFeed to true for tagged playlists as for all uses of this variable, a tagged playlist is the ' same as a live media feed. liveDataFeed.isLiveMediaFeed = true else liveDataFeed.url = newParameterValue(bsp, liveDataFeedXML.url.parameterValue) endif ' get parser functions parserPluginName = liveDataFeedXML.parserPluginName.GetText() uvParserPluginName = liveDataFeedXML.uvParserPluginName.GetText() ' look at main parser for feed first - all functions "should" be in that one ' older versions of BA were able to specify a second parser for User Vars, however, so we check for that if necessary parser = getParserPlugin(bsp, parserPluginName) if parser <> invalid then liveDataFeed.parser$ = parser.parseFeedFunction$ liveDataFeed.uvParser$ = parser.parseUVFunction$ liveDataFeed.customUserAgent$ = parser.userAgentFunction$ if liveDataFeed.uvParser$ = "" then ' Backward compatibility check - see if UV parser plugin separately defined parser = getParserPlugin(bsp, uvParserPluginName) if parser <> invalid then liveDataFeed.uvParser$ = parser.parseUVFunction$ endif endif endif liveDataFeed.updateInterval% = int(val(liveDataFeedXML.updateInterval.GetText())) if LCase(liveDataFeedXML.useHeadRequest.GetText()) = "true" then liveDataFeed.useHeadRequest = true else liveDataFeed.useHeadRequest = false endif liveDataFeed.usage$ = "undefined" if LCase(liveDataFeedXML.downloadContent.GetText()) = "true" then liveDataFeed.usage$ = "content" else if liveDataFeedXML.dataFeedUse.GetText() <> "" then liveDataFeed.usage$ = LCase(liveDataFeedXML.dataFeedUse.GetText()) 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 liveDataFeed.isMRSSFeed = true liveDataFeed.usage$ = "mrss" 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% liveDataFeed.useHeadRequest = false 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.customUserAgent$ = "" liveDataFeed.updateInterval% = 0 liveDataFeed.usage$ = "text" liveDataFeed.isMRSSFeed = false liveDataFeed.isDynamicPlaylist = false liveDataFeed.isLiveMediaFeed = false liveDataFeed.headRequest = false liveDataFeed.useHeadRequest = false liveDataFeed.autoGenerateUserVariables = false liveDataFeed.forceUpdate = false liveDataFeed.restrictNumberOfItems = false liveDataFeed.numberOfItemsToDisplay = -1 liveDataFeed.feedContentFilesToDownload = {} return liveDataFeed End Function Sub SetLiveDataFeedHandlers( liveDataFeed As Object ) liveDataFeed.ReadFeedContent = ReadFeedContent liveDataFeed.ReadLiveFeedContent = ReadLiveFeedContent liveDataFeed.ParseSimpleRSSFeed = ParseSimpleRSSFeed liveDataFeed.ReadMRSSContent = ReadMRSSContent liveDataFeed.DownloadLiveFeedContent = DownloadLiveFeedContent liveDataFeed.DownloadMRSSContent = DownloadMRSSContent liveDataFeed.ParseMRSSFeed = ParseMRSSFeed liveDataFeed.FeedIsMRSS = FeedIsMRSS liveDataFeed.RestartLiveDataFeedDownloadTimer = RestartLiveDataFeedDownloadTimer liveDataFeed.HandleLiveDataFeedContentDownloadAssetFetcherEvent = HandleLiveDataFeedContentDownloadAssetFetcherEvent liveDataFeed.HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent = HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent liveDataFeed.ConvertMRSSFormatToContent = ConvertMRSSFormatToContent 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.beacons = { } m.scriptPlugins = CreateObject("roArray", 1, true) m.videoModePlugins = CreateObject("roArray", 1, true) m.parserPlugins = CreateObject("roArray", 1, true) m.additionalPublishedFiles = CreateObject("roArray", 1, true) m.bypassProxyHosts = [] m.IsSyncMaster = false m.IsSyncSlave = false m.rfChannelName = "" m.rfVirtualChannel = "" m.tunerScanPercentageComplete$ = "0" for n% = 0 to 3 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%) ' send a heartbeat when starting a new presentation so that BA->Manage has the latest information as quickly as possible sendHeartbeatMsg = CreateObject("roAssociativeArray") sendHeartbeatMsg["EventType"] = "SEND_HEARTBEAT" m.msgPort.PostMessage(sendHeartbeatMsg) 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 if type(m.sign) = "roAssociativeArray" then ' initialize audio configuration audioConfiguration = CreateObject("roAudioConfiguration") if type(audioConfiguration) = "roAudioConfiguration" then audioConfiguration$ = lcase(m.sign.audioConfiguration$) audioAutoLevel = m.sign.audioAutoLevel if audioAutoLevel = true then m.diagnostics.PrintDebug("Debug: audioAutoLevel is set to true") else if audioAutoLevel = false then m.diagnostics.PrintDebug("Debug: audioAutoLevel is set to false") endif if audioConfiguration$ = "mixedaudiopcmonly" then if audioAutoLevel = true then audioRouting = { mode: "prerouted", autolevel: "on", pcmonly: "true", srcrate: 48000 } else audioRouting = { mode: "prerouted", autolevel: "off", pcmonly: "true", srcrate: 48000 } endif else if audioConfiguration$ = "mixedaudiopcmcompressed" then if audioAutoLevel = true then audioRouting = { mode: "prerouted", autolevel: "on", pcmonly: "false", srcrate: 48000 } else audioRouting = { mode: "prerouted", autolevel: "off", pcmonly: "false", srcrate: 48000 } endif else audioRouting = { mode : "dynamic" } endif ok = audioConfiguration.ConfigureAudio(audioRouting) if not ok then m.diagnostics.PrintDebug("Configure audio failure: " + audioConfiguration$) endif endif endif ' 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 3 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 3 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.usbVolumeA% = 100 m.usbVolumeB% = 100 m.usbVolumeC% = 100 m.usbVolumeD% = 100 m.usbVolumeTypeA% = 100 m.usbVolumeTypeC% = 100 m.usbVolumeA1% = 100 m.usbVolumeA2% = 100 m.usbVolumeA3% = 100 m.usbVolumeA4% = 100 m.usbVolumeA5% = 100 m.usbVolumeA6% = 100 m.usbVolumeA7% = 100 ' reclaim memory RunGarbageCollector() ' 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 userVariables = m.currentUserVariables ' if there are script plugins associated with this sign, initialize them here ERR_NORMAL_END = &hFC for each scriptPlugin in m.scriptPlugins initializeFunction$ = "result = " + scriptPlugin.name$ + "_Initialize(m.msgPort, userVariables, m)" retVal = Eval(initializeFunction$) if type(retVal) = "roList" then ' log the failure m.diagnostics.PrintDebug("Failure executing Eval to initialize script plugin file: error string was " + retVal[0].ERRSTR + ", line number was " + stri(retVal[0].LINENO) + ", call was " + initializeFunction$) m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, retVal[0].ERRSTR + chr(9) + stri(retVal[0].LINENO) + chr(9) + scriptPlugin.name$) else 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 ' 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" else if ext="ogg" return "audio/ogg" else if ext="flac" return "audio/flac" else if ext="aac" return "audio/aac" else if ext="m4a" return "audio/mp4" ' 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" else if ext="mov" return "video/quicktime" else if ext="mkv" return "video/x-matroska" 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.sysInfo.edidMonitorSerialNumber$, postMsg) else if userVariable.systemVariable$ = "edidYearOfManufacture" then userVariable.SetCurrentValue(m.sysInfo.edidYearOfManufacture$, postMsg) else if userVariable.systemVariable$ = "edidMonitorName" then userVariable.SetCurrentValue(m.sysInfo.edidMonitorName$, postMsg) else if userVariable.systemVariable$ = "edidManufacturer" then userVariable.SetCurrentValue(m.sysInfo.edidManufacturer$, postMsg) else if userVariable.systemVariable$ = "edidUnspecifiedText" then userVariable.SetCurrentValue(m.sysInfo.edidUnspecifiedText$, postMsg) else if userVariable.systemVariable$ = "edidSerialNumber" then userVariable.SetCurrentValue(m.sysInfo.edidSerialNumber$, postMsg) else if userVariable.systemVariable$ = "edidManufacturerProductCode" then userVariable.SetCurrentValue(m.sysInfo.edidManufacturerProductCode$, postMsg) else if userVariable.systemVariable$ = "edidWeekOfManufacture" then userVariable.SetCurrentValue(m.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") rebootRequired = false for each boseProductXML in boseProductsXML registryKeyWritten = AddBoseProduct(boseProducts, boseProductXML) rebootRequired = rebootRequired or registryKeyWritten next if rebootRequired then usbRegistrySection = CreateObject("roRegistrySection", "usb") if type(usbRegistrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop usbRegistrySection.Flush() RebootSystem() endif return boseProducts End Function Function AddBoseProduct(boseProducts As Object, boseProductXML As Object) rebootRequired = false boseProduct = CreateObject("roAssociativeArray") productName$ = boseProductXML.productName.GetText() tapProtocol$ = boseProductXML.tapProtocol.GetText() if lcase(tapProtocol$) = "hid" then boseProduct.usbHIDCommunication = true else boseProduct.usbHIDCommunication = false endif boseProduct.usbAudioInterfaceIndex$ = boseProductXML.usbAudioInterfaceIndex.GetText() if boseProduct.usbAudioInterfaceIndex$ <> "" then boseProduct.usbAudio = true else boseProduct.usbAudio = false endif boseProduct.usbTapInterfaceIndex$ = boseProductXML.usbTapInterfaceIndex.GetText() boseProduct.tapProtocol = boseProductXML.tapProtocol.GetText() boseProduct.usbAsyncAudio = false usbAsyncAudio$ = boseProductXML.usbAsyncAudio.GetText() if lcase(usbAsyncAudio$) = "true" then boseProduct.usbAsyncAudio = true endif if type(boseProductXML.usbToSerial) = "roXMLList" and boseProductXML.usbToSerial.Count() > 0 then usbRegistrySection = CreateObject("roRegistrySection", "usb") if type(usbRegistrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop for each usbToSerialEntry in boseProductXML.usbToSerial registryKey$ = usbToSerialEntry.registryKey[0].getText() registryValue$ = usbToSerialEntry.registryValue[0].getText() existingRegistryValue$ = usbRegistrySection.Read(registryKey$) if registryValue$ <> existingRegistryValue$ then usbRegistrySection.Write(registryKey$, registryValue$) rebootRequired = true endif next endif 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) return rebootRequired End Function 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$ ' 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 if state.type$ = "mediaList" then for each cmd in state.transitionNextItemCmds m.CreateObjectForTransitionCommand(cmd) next for each cmd in state.transitionPrevItemCmds m.CreateObjectForTransitionCommand(cmd) next if type(state.mediaListEndEvent) = "roAssociativeArray" then m.CreateObjectsNeededForTransitionCommands(state.mediaListEndEvent) endif 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 3 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.signChannelEndEvent) = "roAssociativeArray" m.CreateObjectsNeededForTransitionCommands(state.signChannelEndEvent) end if 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.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) protocol$ = "" if IsUsbCommunicationPort(serialPort) then if GetGlobalAA().usbHIDPortConfigurations.DoesExist(serialPort) then usbHIDPortConfiguration = GetGlobalAA().usbHIDPortConfigurations[serialPort] protocol$ = usbHIDPortConfiguration.protocol$ endif else port% = int(val(serialPort)) serialPortConfiguration = m.bsp.sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ endif 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 if protocol$ <> "" then 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.audioTimeCodeEvents) = "roAssociativeArray" then for each eventNum in state.audioTimeCodeEvents m.CreateObjectsNeededForTransitionCommands(state.audioTimeCodeEvents[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.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.diagnostics.PrintDebug("@@@ roSyncManager created. Value of ptp domain:" + aa.Domain) m.bsp.SyncManager.SetMasterMode(m.bsp.IsSyncMaster) if m.bsp.IsSyncSlave then m.bsp.SyncManager.SetPort(m.bsp.msgPort) m.bsp.diagnostics.PrintDebug("@@@ Node is a slave") endif if m.bsp.setSyncDomainSupported then if m.bsp.videomode.SetSyncDomain(m.bsp.sign.ptpDomain$) then m.bsp.diagnostics.PrintDebug("@@@ VSYNC Enabled on Domain @@@ : " + m.bsp.sign.ptpDomain$) else m.bsp.diagnostics.PrintDebug("@@@ VSYNC failed to enable on Domain @@@ : " + m.bsp.sign.ptpDomain$ + " : " + m.bsp.videomode.GetFailureReason()) end if endif endif End Sub Sub CreateCommunicationObjects(cmd As Object) commandName$ = cmd.name$ if commandName$ = "sendUDPCommand" or commandName$ = "sendUDPBytesCommand" 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$ = "sendUDPBytesCommand" 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", 4, true) m.bpOutputSetup = CreateObject("roArray", 4, 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") m.ConfigureBP(3, "TouchBoard-3-LED", "TouchBoard-3-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.bpOutput[buttonPanelIndex%].SetUserData(touchBoardLED$) m.bpOutputSetup[buttonPanelIndex%] = CreateObject("roControlPort", touchBoardLEDSetup$) if type(m.bpOutputSetup[buttonPanelIndex%]) = "roControlPort" then m.bpOutputSetup[buttonPanelIndex%].SetUserData(touchBoardLEDSetup$) 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 if bsp.udpAddressType$ = "Ethernet" then bsp.udpSender.SetDestination("BCAST-SUBNET-0", bsp.udpSendPort) else if bsp.udpAddressType$ = "Wireless" then bsp.udpSender.SetDestination("BCAST-SUBNET-1", 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 ScheduleRetryCreateSerial(port$ As String, outputOnly As Boolean) if type(m.serialPortsToRetry) <> "roAssociativeArray" then m.serialPortsToRetry = {} endif if not m.serialPortsToRetry.DoesExist(port$) then aa = {} aa.port$ = port$ aa.outputOnly = outputOnly timer = CreateObject("roTimer") timer.SetPort(m.msgPort) timer.SetElapsed(15, 0) timer.Start() aa.timer = timer m.serialPortsToRetry[port$] = aa m.diagnostics.PrintDebug("ScheduleRetryCreateSerial on port " + port$) endif End Sub Function RetryCreateSerial(port$ As String, outputOnly As Boolean) As Boolean ok = m.AttemptOpenSerial(port$, outputOnly) m.diagnostics.PrintDebug("RetryCreateSerial on port " + port$) if ok and m.serialPortsToRetry.DoesExist(port$) then m.serialPortsToRetry.Delete(port$) endif return ok End Function Sub CreateSerial(bsp As Object, port$ As String, outputOnly As Boolean) ok = bsp.AttemptOpenSerial(port$, outputOnly) if not ok then bsp.ScheduleRetryCreateSerial(port$, outputOnly) endif End Sub Function IsUsbCommunicationPort(port$ As String) As Boolean port = val(port$) if port = 0 and port$ <> "0" then return true return false End Function Function AttemptOpenSerial(port$ As String, outputOnly As Boolean) As Boolean if type(m.serial) <> "roAssociativeArray" then m.serial = CreateObject("roAssociativeArray") endif if type(m.serialOutputOnlySpec) <> "roAssociativeArray" then m.serialOutputOnlySpec = CreateObject("roAssociativeArray") endif ' determine if the desired port refers to a port number or a usb port if m.usbDevicesByConnector.DoesExist(port$) then port$ = m.usbDevicesByConnector.Lookup(port$) endif if IsUsbCommunicationPort(port$) then if GetGlobalAA().usbCDCPortConfigurations.DoesExist(port$) then usbCDCPortConfiguration = GetGlobalAA().usbCDCPortConfigurations[port$] serialPortSpeed% = usbCDCPortConfiguration.serialPortSpeed sendEol$ = usbCDCPortConfiguration.sendEol$ receiveEol$ = usbCDCPortConfiguration.receiveEol$ protocol$ = usbCDCPortConfiguration.protocol$ usbPort$ = "USB:" + right(port$, len(port$) - 4) + "." + usbCDCPortConfiguration.usbTapInterfaceIndex$ if type(m.serial[port$]) = "roSerialPort" then serial = m.serial[port$] else serial = CreateObject("roSerialPort", usbPort$, serialPortSpeed%) if type(serial) <> "roSerialPort" then m.diagnostics.PrintDebug("Error creating roSerialPort " + usbPort$) return false endif m.serialOutputOnlySpec[port$] = true endif else if GetGlobalAA().usbHIDPortConfigurations.DoesExist(port$) then usbHIDPortConfiguration = GetGlobalAA().usbHIDPortConfigurations[port$] sendEol$ = usbHIDPortConfiguration.sendEol$ receiveEol$ = usbHIDPortConfiguration.receiveEol$ protocol$ = usbHIDPortConfiguration.protocol$ usbPort$ = "USB:" + right(port$, len(port$) - 4) + "." + usbHIDPortConfiguration.usbTapInterfaceIndex$ if type(m.serial[port$]) = "roUsbTap" then serial = m.serial[port$] else serial = CreateObject("roUsbTap", usbPort$) if type(serial) <> "roUsbTap" then m.diagnostics.PrintDebug("Error creating roUsbTap " + usbPort$) return false endif m.serialOutputOnlySpec[port$] = true endif else return false endif else port% = int(val(port$)) serialPortConfiguration = m.serialPortConfigurations[port%] serialPortSpeed% = serialPortConfiguration.serialPortSpeed% serialPortMode$ = serialPortConfiguration.serialPortMode$ if type(m.serial[port$]) = "roSerialPort" then serial = m.serial[port$] else serial = CreateObject("roSerialPort", port%, serialPortSpeed%) if type(serial) <> "roSerialPort" then m.diagnostics.PrintDebug("Error creating roSerialPort " + port$) return false endif m.serialOutputOnlySpec[port$] = true endif ok = serial.SetMode(serialPortMode$) if not ok then print "Error setting serial mode" : return false protocol$ = serialPortConfiguration.protocol$ sendEol$ = serialPortConfiguration.sendEol$ receiveEol$ = serialPortConfiguration.receiveEol$ if serialPortConfiguration.invertSignals then serial.SetInverted(1) else serial.SetInverted(0) endif endif serial.SetSendEol(sendEol$) serial.SetReceiveEol(receiveEol$) serial.SetUserData(port$) m.serial[port$] = serial if not outputOnly then m.serialOutputOnlySpec[port$] = false if protocol$ = "Binary" then serial.SetByteEventPort(m.msgPort) else serial.SetLineEventPort(m.msgPort) endif endif return true End Function Function IsNonEmptyString(inputVariable As Object) As Boolean if not IsString(inputVariable) then return false if len(inputVariable) = 0 then return false return true End Function Function IsString(inputVariable As Object) As Boolean if type(inputVariable) = "roString" or type(inputVariable) = "String" then return true return false End Function Function IsBoolean(inputVariable as object) As Boolean if type(inputVariable) = "roBoolean" or type(inputVariable) = "Boolean" 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 Function CanUseScreenModes(videoMode as object) as boolean if not IsMultiScreenModel() then return false if type(videoMode) <> "roVideoMode" then videoMode = CreateObject("roVideoMode") ' GetScreenModes is a function introduced in Series 5 players. ' Therefore we need to check if it's supported before use to avoid errors in lower series. if type(videoMode) = "roVideoMode" and findMemberFunction(videoMode, "GetScreenModes") <> invalid then screenModes = videoMode.GetScreenModes() if type(screenModes) = "roArray" and screenModes.Count() > 0 then return true end if return false end function Function IsMultiScreenModel() as boolean di = CreateObject("roDeviceInfo") if di <> invalid then if di.HasFeature("HDMI-2") then ' if a model has HDMI-2, it supports multiple screens return true end if end if return false end function 'region newSign Function newSign(BrightAuthor As Object, globalVariables As Object, bsp As Object, msgPort As Object, controlPort As Object, version% As Integer) As Object publishedModel$ = UCase(BrightAuthor.meta.model.GetText()) if publishedModel$ = "HD110" or publishedModel$ = "HD210" or publishedModel$ = "HD410" or publishedModel$ = "HD1010" or publishedModel$ = "HD1010W" or publishedModel$ = "TD1012" or publishedModel$ = "A913" or publishedModel$ = "A933" or publishedModel$ = "HD910" or publishedModel$ = "HD912" or publishedModel$ = "HD960" or publishedModel$ = "HD962" then DisplayObsoleteModelMessage(bsp, publishedModel$) endif ' determine whether this presentation could use new or old parameters (a Panther might use either) bsp.currentPresentationUsesRoAudioOutputParameters = true ' reset variable bsp.mediaListInactivity = invalid Sign = CreateObject("roAssociativeArray") Sign.numTouchEvents% = 0 Sign.numAudioTimeCodeEvents% = 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 plugins videoModePluginsContainer = BrightAuthor.meta.videoModePlugins if videoModePluginsContainer.Count() = 1 then videoModePluginsXML = videoModePluginsContainer.GetChildElements() for each videoModePluginXML in videoModePluginsXML videoModePlugin = newVideoModePlugin(videoModePluginXML) bsp.videoModePlugins.push(videoModePlugin) 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 videoMode = CreateObject("roVideoMode") if CanUseScreenModes(videoMode) then screenModes = videoMode.GetScreenModes() ' Update the corresponding settings to the OS video output array ' BA Classic only supports 1 screen, so we'll update only index 0 videoOutputOSIndex% = 0 videoMode$ = BrightAuthor.meta.videoMode.GetText() forceResolution = false if BrightAuthor.meta.forceResolution.GetText() <> "" then if lcase(BrightAuthor.meta.forceResolution.GetText()) = "true" then forceResolution = true endif endif if not forceResolution and bsp.forceResolutionSupported then videoMode$ = videoMode$ + ":preferred" endif if BrightAuthor.meta.dolbyVision.GetText() <> "" then if lcase(BrightAuthor.meta.dolbyVision.GetText()) = "true" then videoMode$ = videoMode$ + ":dbv" endif endif if BrightAuthor.meta.fullResGraphicsEnabled.GetText() <> "" then if lcase(BrightAuthor.meta.fullResGraphicsEnabled.GetText()) = "true" and bsp.fullResolutionGraphicsSupported then videoMode$ = videoMode$ + ":fullres" endif endif if BrightAuthor.meta.tenBitColorEnabled.GetText() <> "" then if lcase(BrightAuthor.meta.tenBitColorEnabled.GetText()) = "true" then videoMode$ = videoMode$ + ":10bit" endif endif screenModes[videoOutputOSIndex%].video_mode = videoMode$ ' transform has options normal, 90, 180 or 270 orientation$ = lcase(BrightAuthor.meta.monitorOrientation.GetText()) if orientation$ = "portrait" then screenModes[videoOutputOSIndex%].transform = "90" else if orientation$ = "portraitbottomonright" then screenModes[videoOutputOSIndex%].transform = "270" else screenModes[videoOutputOSIndex%].transform = "normal" end if screenModes[videoOutputOSIndex%].display_x = 0 screenModes[videoOutputOSIndex%].display_y = 0 screenModes[videoOutputOSIndex%].enabled = true ' disable all HDMI ports except 0th index for i% = 1 to screenModes.Count() - 1 screenModes[i%].enabled = false next ' allow videoModePlugin to set multi screens videoMode videoModeInputs = {} videoModeInputs.screenModes = screenModes screenModes = parseVideoModePlugin(videoModeInputs, bsp, "", screenModes) ok = videoMode.SetScreenModes(screenModes) ' error handling in case it fails if ok = 0 then errMsg$ = "Error: Can't set VIDEOMODE screen array. Resetting to 1920x1080x60i" bsp.diagnostics.PrintDebug(errMsg$) bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SET_VIDEO_MODE, errMsg$) videoMode.SetMode("1920x1080x60i") end if ' Video walls should be supported on all Series 5 players since only 1 screen is supported in BA Classic 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 else ' players do not support multi screens or SetScreenModes() 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$ videoMode$ = Sign.videoMode$ forceResolution = false if BrightAuthor.meta.forceResolution.GetText() <> "" then if lcase(BrightAuthor.meta.forceResolution.GetText()) = "true" then forceResolution = true endif endif setPreferredResolution = false if not forceResolution and bsp.forceResolutionSupported then setPreferredResolution = true videoMode$ = videoMode$ + ":preferred" endif dolbyVisionEnabled = false if BrightAuthor.meta.dolbyVision.GetText() <> "" then if lcase(BrightAuthor.meta.dolbyVision.GetText()) = "true" then videoMode$ = videoMode$ + ":dbv" dolbyVisionEnabled = true endif endif fullResGraphicsEnabled = false if BrightAuthor.meta.fullResGraphicsEnabled.GetText() <> "" then if lcase(BrightAuthor.meta.fullResGraphicsEnabled.GetText()) = "true" and bsp.fullResolutionGraphicsSupported then fullResGraphicsEnabled = true videoMode$ = videoMode$ + ":fullres" endif endif tenBitColorEnabled = false if BrightAuthor.meta.tenBitColorEnabled.GetText() <> "" then if lcase(BrightAuthor.meta.tenBitColorEnabled.GetText()) = "true" then tenBitColorEnabled = true videoMode$ = videoMode$ + ":10bit" endif endif ' allow videoModePlugin to set videoMode videoModeInputs = {} videoModeInputs.signVideoMode$ = Sign.videoMode$ videoModeInputs.setPreferredResolution = setPreferredResolution videoModeInputs.fullResGraphicsEnabled = fullResGraphicsEnabled videoModeInputs.tenBitColorEnabled = tenBitColorEnabled videoModeInputs.dolbyVisionEnabled = dolbyVisionEnabled videoModeInputs.videoMode$ = videoMode$ ' allow videoModePlugin to set single screen videoMode singleVideoMode = parseVideoModePlugin(videoModeInputs, bsp, videoMode$, {}) ok = videoMode.SetMode(singleVideoMode) if ok = 0 then print "Error: Can't set VIDEOMODE to ::"; Sign.videoMode$; " resetting to 1920x1080x60i" ok = videoMode.SetMode("1920x1080x60i") endif if bsp.forceResolutionSupported then aa = videoMode.GetConfiguredMode() if aa <> invalid then bsp.configuredResX = aa.graphicsPlaneWidth bsp.configuredResY = aa.graphicsPlaneHeight else bsp.configuredResX = videoMode.GetResX() bsp.configuredResY = videoMode.GetResY() endif bsp.diagnostics.PrintDebug("Specified videoMode: " + singleVideoMode + ", actual videoMode: " + videoMode.GetMode()) bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_SET_VIDEO_MODE, "Specified videoMode: " + singleVideoMode + ", actual videoMode: " + videoMode.GetMode()) else bsp.configuredResX = videoMode.GetResX() bsp.configuredResY = videoMode.GetResY() endif ' if the user specified a video mode other than what the system chose, scale screen items as needed bsp.actualResX = videoMode.GetResX() bsp.actualResY = videoMode.GetResY() Sign.videoConnector$ = BrightAuthor.meta.videoConnector.GetText() if not IsString(Sign.videoConnector$) then print "Invalid XML file - meta videoConnector not found" : stop 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 endif 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 ' maintain for backwards compatibilities with old projects Sign.htmlEnableLocalStorage = false if lcase(BrightAuthor.meta.htmlEnableLocalStorage.GetText()) = "true" then Sign.htmlEnableLocalStorage = true Sign.htmlLocalStorageSize% = int(val(BrightAuthor.meta.htmlLocalStorageSize.GetText())) 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", 8, true) GetGlobalAA().usbCDCPortConfigurations = {} GetGlobalAA().usbHIDPortConfigurations = {} GetGlobalAA().usbAudioPortConfigurations = {} bp900AConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900AConfigureAutomatically.GetText(), true) bp900BConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900BConfigureAutomatically.GetText(), true) bp900CConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900CConfigureAutomatically.GetText(), true) bp900DConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP900DConfigureAutomatically.GetText(), true) bp200AConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200AConfigureAutomatically.GetText(), true) bp200BConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200BConfigureAutomatically.GetText(), true) bp200CConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200CConfigureAutomatically.GetText(), true) bp200DConfigureAutomatically = GetBoolFromString(BrightAuthor.meta.BP200DConfigureAutomatically.GetText(), true) bp900AConfiguration% = GetIntFromString(BrightAuthor.meta.BP900AConfiguration.GetText()) bp900BConfiguration% = GetIntFromString(BrightAuthor.meta.BP900BConfiguration.GetText()) bp900CConfiguration% = GetIntFromString(BrightAuthor.meta.BP900CConfiguration.GetText()) bp900DConfiguration% = GetIntFromString(BrightAuthor.meta.BP900DConfiguration.GetText()) bp200AConfiguration% = GetIntFromString(BrightAuthor.meta.BP200AConfiguration.GetText()) bp200BConfiguration% = GetIntFromString(BrightAuthor.meta.BP200BConfiguration.GetText()) bp200CConfiguration% = GetIntFromString(BrightAuthor.meta.BP200CConfiguration.GetText()) bp200DConfiguration% = GetIntFromString(BrightAuthor.meta.BP200DConfiguration.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 if type(bsp.bpInputPorts[3]) = "roControlPort" then bsp.bpInputPortConfigurations[3] = GetBPConfiguration(bsp.bpInputPortHardware[3], bp900DConfigureAutomatically, bp900DConfiguration%, bp200DConfigureAutomatically, bp200DConfiguration%) 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 parser plugins parserPluginsContainer = BrightAuthor.meta.parserPlugins if parserPluginsContainer.Count() = 1 then parserPluginsXML = parserPluginsContainer.GetChildElements() for each parserPluginXML in parserPluginsXML parserPlugin = newParserPlugin(parserPluginXML) bsp.parserPlugins.push(parserPlugin) 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 userVariable.systemVariable$ = userVariableXML.systemVariable.GetText() ' 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 if bsp.liveDataFeeds.IsEmpty() then if type(bsp.networkingHSM) = "roAssociativeArray" then bsp.networkingHSM.UploadDeviceDownloadProgressFileList() bsp.networkingHSM.FileListPendingUpload = false endif 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 ' assign system variables to user variables bsp.AssignSystemVariablesToUserVariables() ' 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 ' parse beacons beaconsContainer = BrightAuthor.meta.beacons if beaconsContainer <> invalid and beaconsContainer.Count() = 1 then if bsp.btManager.ParsePresentationBeacons(beaconsContainer.GetChildElements()) then ' If presentation beacons were defined, set advertising to start any marked for autostart bsp.btManager.SetBtAdvertising() endif 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 boseProduct section - lists Bose products in use in this presentation gaa = GetGlobalAA() boseProductsXML = BrightAuthor.meta.boseProduct if boseProductsXML.Count() > 0 then Sign.boseProducts = CreateObject("roAssociativeArray") Sign.boseProductsByConnector = {} for each boseProductXML in boseProductsXML boseProduct = CreateObject("roAssociativeArray") boseProduct.productName$ = boseProductXML.productName.GetText() boseProduct.port$ = boseProductXML.port.GetText() Sign.boseProducts.AddReplace(boseProduct.productName$, boseProduct) Sign.boseProductsByConnector.AddReplace(boseProduct.port$, boseProduct) boseProductSpec = bsp.GetBoseProductSpec(boseProduct.productName$) if type(boseProductSpec) = "roAssociativeArray" then if boseProductSpec.tapProtocol = "CDC" then usbCDCPortConfiguration = {} usbCDCPortConfiguration.sendEol$ = GetEolFromSpec(boseProductSpec.sendEol$) usbCDCPortConfiguration.receiveEol$ = GetEolFromSpec(boseProductSpec.receiveEol$) usbCDCPortConfiguration.protocol$ = boseProductSpec.protocol$ usbCDCPortConfiguration.usbTapInterfaceIndex$ = boseProductSpec.usbTapInterfaceIndex$ usbCDCPortConfiguration.serialPortSpeed = boseProductSpec.baudRate% gaa.usbCDCPortConfigurations.AddReplace(boseProduct.port$, usbCDCPortConfiguration) else if boseProductSpec.tapProtocol = "HID" then usbHIDPortConfiguration = {} usbHIDPortConfiguration.sendEol$ = GetEolFromSpec(boseProductSpec.sendEol$) usbHIDPortConfiguration.receiveEol$ = GetEolFromSpec(boseProductSpec.receiveEol$) usbHIDPortConfiguration.protocol$ = boseProductSpec.protocol$ usbHIDPortConfiguration.usbTapInterfaceIndex$ = boseProductSpec.usbTapInterfaceIndex$ gaa.usbHIDPortConfigurations.AddReplace(boseProduct.port$, usbHIDPortConfiguration) else 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 if boseProductSpec.usbAudioInterfaceIndex$ <> "" then usbAudioPortConfiguration = {} usbAudioPortConfiguration.usbAudioInterfaceIndex$ = boseProductSpec.usbAudioInterfaceIndex$ gaa.usbAudioPortConfigurations.AddReplace(boseProduct.port$, usbAudioPortConfiguration) endif endif next endif ' initialize USB name mapping gaa.baUSBConfigNameToDeviceName = {} gaa.baUSBConfigNameToDeviceName["usbA"] = "USB A" gaa.baUSBConfigNameToDeviceName["usbB"] = "USB B" gaa.baUSBConfigNameToDeviceName["usbC"] = "USB C" gaa.baUSBConfigNameToDeviceName["usbD"] = "USB D" gaa.baUSBConfigNameToDeviceName["usb type_a"] = "USB Type_A" gaa.baUSBConfigNameToDeviceName["usb type_c"] = "USB Type_C" gaa.baUSBConfigNameToDeviceName["usb700_1"] = "USB 700_1" gaa.baUSBConfigNameToDeviceName["usb700_2"] = "USB 700_2" gaa.baUSBConfigNameToDeviceName["usb700_3"] = "USB 700_3" gaa.baUSBConfigNameToDeviceName["usb700_4"] = "USB 700_4" gaa.baUSBConfigNameToDeviceName["usb700_5"] = "USB 700_5" gaa.baUSBConfigNameToDeviceName["usb700_6"] = "USB 700_6" gaa.baUSBConfigNameToDeviceName["usb700_7"] = "USB 700_7" gaa.baUSBParamNameToDeviceParamName = {} gaa.baUSBParamNameToDeviceParamName["usbA"] = "usbA" gaa.baUSBParamNameToDeviceParamName["usbB"] = "usbB" gaa.baUSBParamNameToDeviceParamName["usbC"] = "usbC" gaa.baUSBParamNameToDeviceParamName["usbD"] = "usbD" gaa.baUSBParamNameToDeviceParamName["usbTypeA"] = "usbTypeA" gaa.baUSBParamNameToDeviceParamName["usbTypeC"] = "usbTypeC" gaa.baUSBParamNameToDeviceParamName["usb700_1"] = "usbA1" gaa.baUSBParamNameToDeviceParamName["usb700_2"] = "usbA2" gaa.baUSBParamNameToDeviceParamName["usb700_3"] = "usbA3" gaa.baUSBParamNameToDeviceParamName["usb700_4"] = "usbA4" gaa.baUSBParamNameToDeviceParamName["usb700_5"] = "usbA5" gaa.baUSBParamNameToDeviceParamName["usb700_6"] = "usbA6" gaa.baUSBParamNameToDeviceParamName["usb700_7"] = "usbA7" gaa.usbParamNameToDeviceParamName = {} gaa.usbParamNameToDeviceParamName["a1"] = "A/1" gaa.usbParamNameToDeviceParamName["a2"] = "A/2" gaa.usbParamNameToDeviceParamName["a3"] = "A/3" gaa.usbParamNameToDeviceParamName["a4"] = "A/4" gaa.usbParamNameToDeviceParamName["a5"] = "A/5" gaa.usbParamNameToDeviceParamName["a6"] = "A/6" gaa.usbParamNameToDeviceParamName["a7"] = "A/7" ' Get the USB topology of the device and create a mapping of Bose port names in the presentation to USB device names usbMappings = BuildUSBDevicesByConnector(sign) bsp.usbDevicesByConnector = usbMappings.usbDevicesByConnector ' TODO - HACK gaa.usbDevicesByConnector = usbMappings.usbDevicesByConnector bsp.usbBACommandToDeviceCommandMapping = usbMappings.usbBACommandToDeviceCommandMapping bsp.usbConnectorToConnectorParameterMapping = usbMappings.usbConnectorToConnectorParameterMapping connectedBoseProducts = {} usbCDCPortConfigurations = {} usbHIDPortConfigurations = {} usbAudioPortConfigurations = {} for each connector in Sign.boseProductsByConnector boseProduct = Sign.boseProductsByConnector[connector] if bsp.boseProductSpecs[boseproduct.productname$].usbAsyncAudio then audioConfiguration = CreateObject("roAudioConfiguration") audioConfiguration.ConfigureAudio({ usbasync: 1}) endif if bsp.usbDevicesByConnector.DoesExist(connector) then boseDeviceName = bsp.usbDevicesByConnector[connector] boseProduct.port$ = boseDeviceName connectedBoseProducts.AddReplace(boseDeviceName, boseProduct) if gaa.usbCDCPortConfigurations.DoesExist(connector) then usbCDCPortConfiguration = gaa.usbCDCPortConfigurations.Lookup(connector) usbCDCPortConfigurations.AddReplace(boseDeviceName, usbCDCPortConfiguration) endif if gaa.usbHIDPortConfigurations.DoesExist(connector) then usbHIDPortConfiguration = gaa.usbHIDPortConfigurations.Lookup(connector) usbHIDPortConfigurations.AddReplace(boseDeviceName, usbHIDPortConfiguration) endif if gaa.usbAudioPortConfigurations.DoesExist(connector) then usbAudioPortConfiguration = gaa.usbAudioPortConfigurations.Lookup(connector) usbAudioPortConfigurations.AddReplace(boseDeviceName, usbAudioPortConfiguration) endif endif next ' substitute values in look up tables - change from BA names to actual USB device names Sign.boseProductsByConnector = connectedBoseProducts gaa.usbHIDPortConfigurations = usbHIDPortConfigurations gaa.usbCDCPortConfigurations = usbCDCPortConfigurations gaa.usbAudioPortConfigurations = usbAudioPortConfigurations bsp.boseUSBAudioDevicesByConnector = BuildBoseUSBAudioDevicesByConnector(Sign) 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", 8, true) for i% = 0 to 7 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$) bsp.diagnostics.PrintDebug("@@@ PTP domain value written to registry:" + 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() if IsControlPort(controlPort) then 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 endif audioInSampleRate$ = BrightAuthor.meta.audioInSampleRate.GetText() if audioInSampleRate$ <> "" then Sign.audioInSampleRate% = int(val(audioInSampleRate$)) else Sign.audioInSampleRate% = 48000 endif audioConfiguration$ = BrightAuthor.meta.audioConfiguration.GetText() if audioConfiguration$ = "" then audioConfiguration$ = "FixedAudio" endif Sign.audioConfiguration$ = audioConfiguration$ audioAutoLevel$ = lcase(BrightAuthor.meta.audioAutoLevel.GetText()) if audioAutoLevel$ = "true" then Sign.audioAutoLevel = true else Sign.audioAutoLevel = false 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 usbAMinVolume$ = BrightAuthor.meta.usbAMinVolume.GetText() if usbAMinVolume$ <> "" then Sign.usbAMinVolume% = int(val(usbAMinVolume$)) else Sign.usbAMinVolume% = 0 endif usbAMaxVolume$ = BrightAuthor.meta.usbAMaxVolume.GetText() if usbAMaxVolume$ <> "" then Sign.usbAMaxVolume% = int(val(usbAMaxVolume$)) else Sign.usbAMaxVolume% = 100 endif usbBMinVolume$ = BrightAuthor.meta.usbBMinVolume.GetText() if usbBMinVolume$ <> "" then Sign.usbBMinVolume% = int(val(usbBMinVolume$)) else Sign.usbBMinVolume% = 0 endif usbBMaxVolume$ = BrightAuthor.meta.usbBMaxVolume.GetText() if usbBMaxVolume$ <> "" then Sign.usbBMaxVolume% = int(val(usbBMaxVolume$)) else Sign.usbBMaxVolume% = 100 endif usbCMinVolume$ = BrightAuthor.meta.usbCMinVolume.GetText() if usbCMinVolume$ <> "" then Sign.usbCMinVolume% = int(val(usbCMinVolume$)) else Sign.usbCMinVolume% = 0 endif usbCMaxVolume$ = BrightAuthor.meta.usbCMaxVolume.GetText() if usbCMaxVolume$ <> "" then Sign.usbCMaxVolume% = int(val(usbCMaxVolume$)) else Sign.usbCMaxVolume% = 100 endif usbDMinVolume$ = BrightAuthor.meta.usbDMinVolume.GetText() if usbDMinVolume$ <> "" then Sign.usbDMinVolume% = int(val(usbDMinVolume$)) else Sign.usbDMinVolume% = 0 endif usbDMaxVolume$ = BrightAuthor.meta.usbDMaxVolume.GetText() if usbDMaxVolume$ <> "" then Sign.usbDMaxVolume% = int(val(usbDMaxVolume$)) else Sign.usbDMaxVolume% = 100 endif usbA1MinVolume$ = BrightAuthor.meta.usb700_1MinVolume.GetText() if usbA1MinVolume$ <> "" then Sign.usbA1MinVolume% = int(val(usbA1MinVolume$)) else Sign.usbA1MinVolume% = 0 endif usbA1MaxVolume$ = BrightAuthor.meta.usb700_1MaxVolume.GetText() if usbA1MaxVolume$ <> "" then Sign.usbA1MaxVolume% = int(val(usbA1MaxVolume$)) else Sign.usbA1MaxVolume% = 100 endif usbA2MinVolume$ = BrightAuthor.meta.usb700_2MinVolume.GetText() if usbA2MinVolume$ <> "" then Sign.usbA2MinVolume% = int(val(usbA2MinVolume$)) else Sign.usbA2MinVolume% = 0 endif usbA2MaxVolume$ = BrightAuthor.meta.usb700_2MaxVolume.GetText() if usbA2MaxVolume$ <> "" then Sign.usbA2MaxVolume% = int(val(usbA2MaxVolume$)) else Sign.usbA2MaxVolume% = 100 endif usbA3MinVolume$ = BrightAuthor.meta.usb700_3MinVolume.GetText() if usbA3MinVolume$ <> "" then Sign.usbA3MinVolume% = int(val(usbA3MinVolume$)) else Sign.usbA3MinVolume% = 0 endif usbA3MaxVolume$ = BrightAuthor.meta.usb700_3MaxVolume.GetText() if usbA3MaxVolume$ <> "" then Sign.usbA3MaxVolume% = int(val(usbA3MaxVolume$)) else Sign.usbA3MaxVolume% = 100 endif usbA4MinVolume$ = BrightAuthor.meta.usb700_4MinVolume.GetText() if usbA4MinVolume$ <> "" then Sign.usbA4MinVolume% = int(val(usbA4MinVolume$)) else Sign.usbA4MinVolume% = 0 endif usbA4MaxVolume$ = BrightAuthor.meta.usb700_4MaxVolume.GetText() if usbA4MaxVolume$ <> "" then Sign.usbA4MaxVolume% = int(val(usbA4MaxVolume$)) else Sign.usbA4MaxVolume% = 100 endif usbA5MinVolume$ = BrightAuthor.meta.usb700_5MinVolume.GetText() if usbA5MinVolume$ <> "" then Sign.usbA5MinVolume% = int(val(usbA5MinVolume$)) else Sign.usbA5MinVolume% = 0 endif usbA5MaxVolume$ = BrightAuthor.meta.usb700_5MaxVolume.GetText() if usbA5MaxVolume$ <> "" then Sign.usbA5MaxVolume% = int(val(usbA5MaxVolume$)) else Sign.usbA5MaxVolume% = 100 endif usbA6MinVolume$ = BrightAuthor.meta.usb700_6MinVolume.GetText() if usbA6MinVolume$ <> "" then Sign.usbA6MinVolume% = int(val(usbA6MinVolume$)) else Sign.usbA6MinVolume% = 0 endif usbA6MaxVolume$ = BrightAuthor.meta.usb700_6MaxVolume.GetText() if usbA6MaxVolume$ <> "" then Sign.usbA6MaxVolume% = int(val(usbA6MaxVolume$)) else Sign.usbA6MaxVolume% = 100 endif usbA7MinVolume$ = BrightAuthor.meta.usb700_7MinVolume.GetText() if usbA7MinVolume$ <> "" then Sign.usbA7MinVolume% = int(val(usbA7MinVolume$)) else Sign.usbA7MinVolume% = 0 endif usbA7MaxVolume$ = BrightAuthor.meta.usb700_7MaxVolume.GetText() if usbA7MaxVolume$ <> "" then Sign.usbA7MaxVolume% = int(val(usbA7MaxVolume$)) else Sign.usbA7MaxVolume% = 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 mediaListInactivityTimeoutIsGlobal$ = lcase(BrightAuthor.meta.mediaListInactivityTimeoutIsGlobal.GetText()) if mediaListInactivityTimeoutIsGlobal$ = "false" then bsp.mediaListInactivityTimeoutIsGlobal = false else bsp.mediaListInactivityTimeoutIsGlobal = true 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 ' enable zone support here to ensure that SetGraphicsZOrder works EnableZoneSupport(true) graphicsZOrder = BrightAuthor.meta.graphicsZOrder.GetText() if graphicsZOrder <> "" then vm=CreateObject("roVideoMode") vm.SetGraphicsZOrder(lcase(graphicsZOrder)) vm = invalid endif ' mosaic mode / decoders videoMode = CreateObject("roVideoMode") Sign.isMosaic = false isMosaic = BrightAuthor.meta.isMosaic.GetText() if lcase(isMosaic) = "true" then Sign.isMosaic = true mosaicDecoders = BrightAuthor.meta.mosaicDecoders.mosaicDecoder for each mosaicDecoder in mosaicDecoders decoderName = mosaicDecoder.decoderName.GetText() timeSliceMode = mosaicDecoder.timeSliceMode.GetText() zOrder = mosaicDecoder.zOrder.GetText() friendlyName = mosaicDecoder.friendlyName.GetText() enableMosaicDeinterlacer$ = mosaicDecoder.enableMosaicDeinterlacer.GetText() enableMosaicDeinterlacer = false if lcase(enableMosaicDeinterlacer$) = "true" then enableMosaicDeinterlacer = true endif ok = videoMode.SetDecoderMode(decoderName, timeSliceMode, int(val(zOrder)), friendlyName, enableMosaicDeinterlacer) next else if bsp.mosaicModeSupported then decoders=videoMode.GetDecoderModes() for each decoder in decoders videoMode.SetDecoderMode(decoders[0].decoder_name, decoders[0].max_decode_size, 0, decoders[0].decoder_name, false) next endif videoMode = invalid ' 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 ' Support Video Mode Plugin to set videoMode for single and multi screens Function parseVideoModePlugin(videoModeInputs As Object, bsp As Object, singleScreenMode As String, multiScreenModes As Object) As Object ERR_NORMAL_END = &hFC ERR_NO_VALUE_RETURN = 224 ' Value returned when plugin does not contain specified function for each videoModePlugin in bsp.videoModePlugins setVideoModeFunction$ = "result = " + videoModePlugin.functionName$ + "(videoModeInputs, bsp)" retVal = Eval(setVideoModeFunction$) if type(retVal) = "roList" then ' compilation error bsp.diagnostics.PrintDebug("Compilation error invoking Eval to parse VideoMode script plugin: return value = " + stri(retVal)) bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, stri(retVal)) else if retVal <> ERR_NORMAL_END then ' runtime error (function may not exist) ' log the failure bsp.diagnostics.PrintDebug("Failure executing Eval to execute videoMode plugin: return value = " + stri(retVal) + ", call was " + setVideoModeFunction$) bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_SCRIPT_PLUGIN_FAILURE, stri(retVal) + chr(9) + videoModePlugin.fileName$) else videoModeFromPlugin = result ' if videoMode is a string and is not empty, overwrite previously calculated screen mode if isString(videoModeFromPlugin) then if len(videoModeFromPlugin) > 0 then singleScreenMode = videoModeFromPlugin endif ' else if videoMode is a array and is not empty, overwrite previously calculated multi screen modes else if type(videoModeFromPlugin) = "roArray" then if videoModeFromPlugin.count() > 0 then multiScreenModes = videoModeFromPlugin endif endif endif next if singleScreenMode <> "" then return singleScreenMode endif return multiScreenModes End Function '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 Object, postMsg As Boolean) if IsString(value) then value$ = value else ' only convert integers currently value$ = stri(value) endif 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 GetDBTableNamesCallback(resultsData As Object, selectData As Object) selectData.tableNames.AddReplace(resultsData["name"], true) End Sub Function GetDBTableNames() As Object selectData = {} selectData.tableNames = {} select$ = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;" m.ExecuteDBSelect(select$, GetDBTableNamesCallback, selectData, invalid) return selectData.tableNames 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 Function DBTablesExist(existingTables, expectedTables) As Boolean for each tableName in expectedTables if not existingTables.DoesExist(tableName) then m.diagnostics.PrintDebug("Table " + tableName + " does not exist in userVariables.db - reset db") return false endif end for return true End Function Function DbIsValid() As Boolean ' Check for validity of userVariables DB - not an exhaustive check, it merely checks to ensure that the appropriate ' tables are present ' ' Check for existence of schema table. If it doesn't exist, db is invalid ' If it exists, check its value and base subsequent checks on the version ' Check to ensure that the expected tables exist tables = m.GetDBTableNames() ' common tables commonTables = ["SchemaVersion", "Sections"] schema1Tables = ["Variables"] schema2Tables = ["Variables2", "Categories"] if not m.DBTablesExist(tables, commonTables) then return false endif currentSchemaVersion$ = "2.0" existingSchemaVersion$ = m.GetDBVersion() ' check version of existing db schema to determine what tables to check for if existingSchemaVersion$ <> currentSchemaVersion$ then tablesExist = m.DBTablesExist(tables, schema1Tables) else tablesExist = m.DBTablesExist(tables, schema2Tables) endif return tablesExist End Function 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 ok = m.DBIsValid() endif 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 ' in case there is a corrupt file m.diagnostics.PrintDebug("Unable to open userVariables.db, attempt to delete file then create db.") m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_DELETE_USER_VARIABLES_DB, "") ok = DeleteFile("userVariables.db") 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 Sub AssignSystemVariableToUserVariables(userVariable As Object) if userVariable.systemVariable$ = "serialNumber" then userVariable.SetCurrentValue(m.sysInfo.deviceUniqueID$, false) else if userVariable.systemVariable$ = "ipAddressWired" then userVariable.SetCurrentValue(m.sysInfo.ipAddressWired$, false) else if userVariable.systemVariable$ = "ipAddressWireless" then userVariable.SetCurrentValue(m.sysInfo.ipAddressWireless$, false) else if userVariable.systemVariable$ = "firmwareVersion" then userVariable.SetCurrentValue(m.sysInfo.deviceFWVersion$, false) else if userVariable.systemVariable$ = "scriptVersion" then userVariable.SetCurrentValue(m.sysInfo.autorunVersion$, false) else if userVariable.systemVariable$ = "rfChannelCount" then userVariable.SetCurrentValue(StripLeadingSpaces(stri(m.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(m.sysInfo.edidMonitorSerialNumber$, false) else if userVariable.systemVariable$ = "edidYearOfManufacture" then userVariable.SetCurrentValue(m.sysInfo.edidYearOfManufacture$, false) else if userVariable.systemVariable$ = "edidMonitorName" then userVariable.SetCurrentValue(m.sysInfo.edidMonitorName$, false) else if userVariable.systemVariable$ = "edidManufacturer" then userVariable.SetCurrentValue(m.sysInfo.edidManufacturer$, false) else if userVariable.systemVariable$ = "edidUnspecifiedText" then userVariable.SetCurrentValue(m.sysInfo.edidUnspecifiedText$, false) else if userVariable.systemVariable$ = "edidSerialNumber" then userVariable.SetCurrentValue(m.sysInfo.edidSerialNumber$, false) else if userVariable.systemVariable$ = "edidManufacturerProductCode" then userVariable.SetCurrentValue(m.sysInfo.edidManufacturerProductCode$, false) else if userVariable.systemVariable$ = "edidWeekOfManufacture" then userVariable.SetCurrentValue(m.sysInfo.edidWeekOfManufacture$, false) else if userVariable.systemVariable$ = "activePresentation" then if IsString(m.activePresentation$) then userVariable.SetCurrentValue(m.activePresentation$, false) else userVariable.SetCurrentValue("", false) endif else if userVariable.systemVariable$ = "brightAuthorVersion" then userVariable.SetCurrentValue(m.sysInfo.baVersion$, false) endif End Sub Sub AssignSystemVariablesToUserVariables() for each variableName in m.currentUserVariables userVariable = m.currentUserVariables.Lookup(variableName) if type(userVariable) = "roAssociativeArray" then m.AssignSystemVariableToUserVariables(userVariable) endif next 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 GetYesNoFromBool(val As Boolean) As String if val then return "yes" return "no" End Function Function GetBoolFromString(str$ As String, defaultValue As Boolean) As Boolean if str$ = "" then return defaultValue else if lcase(str$) = "true" or lcase(str$) = "yes" 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() zoneHSM.useVideoPlayerForImages = false 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.remainOnCurrentStateActions = "none" remainOnCurrentStateActions$ = transitionXML.remainOnCurrentStateActions.GetText() if remainOnCurrentStateActions$ <> "" then transition.remainOnCurrentStateActions = lcase(remainOnCurrentStateActions$) 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$ = "bp900DUserEvent"or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" or userEventName$ = "bp200DUserEvent" 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 ' convert USB port if necessary if bsp.usbDevicesByConnector.DoesExist(port$) then port$ = bsp.usbDevicesByConnector.Lookup(port$) endif if IsUsbCommunicationPort(port$) then if GetGlobalAA().usbCDCPortConfigurations.DoesExist(port$) then usbCDCPortConfiguration = GetGlobalAA().usbCDCPortConfigurations[port$] protocol$ = usbCDCPortConfiguration.protocol$ else if GetGlobalAA().usbHIDPortConfigurations.DoesExist(port$) then usbHIDPortConfiguration = GetGlobalAA().usbHIDPortConfigurations[port$] protocol$ = usbHIDPortConfiguration.protocol$ else protocol$ = "" endif else port% = int(val(port$)) serialPortConfiguration = sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ endif 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 if protocol$ <> "" then serialPortEvents = serialEvents[port$] serialPortEvents[serial$] = transition endif 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$ = "liveVideo" 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$ = "mediaList" and bsState.mediaType$ = "allMedia" then bsState.videoEndEvent = transition 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$ = "mediaListEnd" then bsState.mediaListEndEvent = transition 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$ = "audioTimeCodeEvent" then if type(bsState.audioTimeCodeEvents) <> "roAssociativeArray" then bsState.audioTimeCodeEvents = CreateObject("roAssociativeArray") endif transition.timeInMS% = int(val(userEvent.parameters.parameter.GetText())) bsState.audioTimeCodeEvents[stri(sign.numAudioTimeCodeEvents%)] = transition sign.numAudioTimeCodeEvents% = sign.numAudioTimeCodeEvents% + 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, zoneHSM, 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, zoneHSM 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$ if conditionalTarget.targetMediaState$ <> "" then targetState = zoneHSM.stateTable[conditionalTarget.targetMediaState$] if targetState.type$ = "superState" and targetState.firstState <> invalid then conditionalTarget.targetMediaState$ = targetState.firstState.id$ endif endif 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.userVariableInTickerItem.Count() = 1 then item = newUserVariablePlaylistItem(bsp, zoneHSM, stateXML.userVariableInTickerItem) 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", 4, true) state.bpEvents[0] = CreateObject("roAssociativeArray") state.bpEvents[1] = CreateObject("roAssociativeArray") state.bpEvents[2] = CreateObject("roAssociativeArray") state.bpEvents[3] = 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, zoneHSM, 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, zoneHSM, 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() ' IMPLEMENT A MORE GENERAL PURPOSE APPROACH, PROBABLY IN BA checkForUSBParameterNames = false if bsCmd.name$ = "muteAudioOutputs" or bsCmd.name$ = "unmuteAudioOutputs" or bsCmd.name$ = "setAllAudioOutputs" then checkForUSBParameterNames = true endif 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 parameterName = parameter.name.GetText() if checkForUSBParameterNames then if GetGlobalAA().baUSBParamNameToDeviceParamName.DoesExist(parameterName) then parameterName = GetGlobalAA().baUSBParamNameToDeviceParamName[parameterName] endif if m.bsp.usbBACommandToDeviceCommandMapping.DoesExist(parameterName) then parameterName = m.bsp.usbBACommandToDeviceCommandMapping[parameterName] endif endif ' if a USB port is specified, perform mapping as needed if parameter.name.GetText() = "port" or parameterName = "connector" then portValue = parameterValue.getCurrentParameterValue() if m.bsp.usbDevicesByConnector.DoesExist(portValue) then portValue$ = m.bsp.usbDevicesByConnector[portValue] parameterValue = newTextParameterValue(portValue$) endif endif bsCmd.parameters.AddReplace(parameterName, 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" and m.isVisible then m.imagePlayer.Show() endif endif if showCanvas then if type(m.canvasWidget) = "roCanvasWidget" and m.isVisible then m.canvasWidget.Show() endif endif if showHtml then if type(m.displayedHtmlWidget) = "roHtmlWidget" and m.isVisible then m.displayedHtmlWidget.Show() endif endif End Sub Sub ShowImageWidget() m.UpdateWidgetVisibility(true, false, false, false, true, false, true) m.imageHidden = false m.canvasHidden = true m.htmlHidden = 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) m.imageHidden = true m.canvasHidden = false m.htmlHidden = true End Sub Sub ShowHtmlWidget() m.UpdateWidgetVisibility(false, true, true, false, true, true, false) m.imageHidden = true m.canvasHidden = true m.htmlHidden = 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$) if type(bsp.encryptionByFile) = "roAssociativeArray" then playlistItemBS.isEncrypted = bsp.encryptionByFile.DoesExist(playlistItemBS.fileName$) else playlistItemBS.isEncrypted = false endif 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, zoneHSM 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.transitionDuration% = 1000 if playlistItemXML.transitionDuration.GetText() <> "" then playlistItemBS.transitionDuration% = int(val(playlistItemXML.transitionDuration.GetText())) endif playlistItemBS.useImageBuffer = false useImageBuffer$ = playlistItemXML.useImageBuffer.GetText() if len(useImageBuffer$) > 0 then useImageBuffer$ = lcase(useImageBuffer$) if useImageBuffer$ = "true" then playlistItemBS.useImageBuffer = true endif endif videoPlayerRequired$ = playlistItemXML.videoPlayerRequired.GetText() if len(videoPlayerRequired$) > 0 then videoPlayerRequired$ = lcase(videoPlayerRequired$) if videoPlayerRequired$ = "true" then zoneHSM.useVideoPlayerForImages = true endif endif state.HStateEventHandler = STDisplayingImageEventHandler state.DisplayImage = DisplayImage state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.ClearVideo = ClearVideo 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 playlistItemBS.automaticallyLoop = true if lcase(playlistItemXML.automaticallyLoop.GetText()) = "false" then playlistItemBS.automaticallyLoop = false 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 state.queryString = htmlSite.queryString else ' what to do here? stop endif state.enableExternalData = false if lcase(playlistItemXML.enableExternalData.GetText()) = "true" then state.enableExternalData = true endif state.enableCamera = false if lcase(playlistItemXML.enableCamera.GetText()) = "true" then state.enableCamera = 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.hwzOn = false if lcase(playlistItemXML.hwzOn.GetText()) = "true" then state.hwzOn = true endif state.enableScrollBars = false if lcase(playlistItemXML.enableScrollBars.GetText()) = "true" then state.enableScrollBars = true endif state.useUserStylesheet = false if lcase(playlistItemXML.useUserStylesheet.GetText()) = "true" then state.useUserStylesheet = true state.userStylesheet = playlistItemXML.userStylesheet.GetText() 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.navigateToLastSelectedOnEntry = false if lcase(playlistItemXML.navigateToLastSelectedOnEntry.GetText()) = "true" then state.navigateToLastSelectedOnEntry = true endif 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.lastInteractiveMenuNavigationIndex% = 0 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() scaleScreenElement(bsp, false, interactiveMenuItem, interactiveMenuItemXML) 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 mediaFileName = "" interactiveMenuItem.targetVideoFile$ = ParseFileNameXML(interactiveMenuItemXML.targetVideoFile) interactiveMenuitem.targetVideoFileUserVariable = bsp.GetUserVariable(interactiveMenuItem.targetVideoFile$) if interactiveMenuItem.targetVideoFile$ <> "" then mediaFileName = interactiveMenuItem.targetVideoFile$ endif interactiveMenuItem.targetAudioFile$ = ParseFileNameXML(interactiveMenuItemXML.targetAudioFile) interactiveMenuitem.targetAudioFileUserVariable = bsp.GetUserVariable(interactiveMenuItem.targetAudioFile$) if interactiveMenuItem.targetAudioFile$ <> "" then mediaFileName = interactiveMenuItem.targetAudioFile$ endif interactiveMenuItem.targetImageFile$ = ParseFileNameXML(interactiveMenuItemXML.targetImageFile) if interactiveMenuItem.targetImageFile$ <> "" then mediaFileName = interactiveMenuItem.targetImageFile$ endif 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 if type(bsp.encryptionByFile) = "roAssociativeArray" and mediaFileName <> "" then interactiveMenuItem.isEncrypted = bsp.encryptionByFile.DoesExist(mediaFileName) else interactiveMenuItem.isEncrypted = false 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$ = "sendUDPBytesCommand" or commandName$ = "synchronize" then bsp.CreateUDPSender(bsp) else if commandName$ = "sendSerialStringCommand" or commandName$ = "sendSerialBlockCommand" or commandName$ = "sendSerialByteCommand" or commandName$ = "sendSerialBytesCommand" then ' TODO - Bose port$? 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() ' convert USB port if necessary if bsp.usbDevicesByConnector.DoesExist(port$) then port$ = bsp.usbDevicesByConnector.Lookup(port$) endif serial$ = userEvent.parameters.parameter2.GetText() if IsUsbCommunicationPort(port$) then ' check if it exists - could be CDC? usbHIDPortConfiguration = GetGlobalAA().usbHIDPortConfigurations[port$] protocol$ = usbHIDPortConfiguration.protocol$ else port% = int(val(port$)) serialPortConfiguration = sign.serialPortConfigurations[port%] protocol$ = serialPortConfiguration.protocol$ endif 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$ = "bp900DUserEvent" or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" or userEventName$ = "bp200DUserEvent" 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 scaleScreenElement(bsp As Object, setDimensions As Boolean, item As Object, itemXML As Object) if bsp.configuredResX <> bsp.actualResX or bsp.configuredResY <> bsp.actualResY then xOffset = val(itemXML.x.GetText()) / bsp.configuredResX x% = xOffset * bsp.actualResX item.x% = x% yOffset = val(itemXML.Y.GetText()) / bsp.configuredResY y% = yOffset * bsp.actualResY item.y% = y% if setDimensions then width% = bsp.actualResX / bsp.configuredResX * val(itemXML.width.GetText()) item.width% = width% height% = bsp.actualResY / bsp.configuredResY * val(itemXML.height.GetText()) item.height% = height% endif else item.x% = int(val(itemXML.x.GetText())) item.y% = int(val(itemXML.y.GetText())) if setDimensions then item.width% = int(val(itemXML.width.GetText())) item.height% = int(val(itemXML.height.GetText())) endif endif End Sub Sub newBaseTemplateItem(templateItem As Object, templateItemXML As Object) ' scale text item if necessary bsp = GetGlobalAA().bsp scaleScreenElement(bsp, true, templateItem, templateItemXML) templateItem.type$ = templateItemXML.GetName() 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 newMRSSMediaTemplateItem(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 else ' custom field if type(state.mrssCustomFieldTemplateItems) <> "roAssociativeArray" then state.mrssCustomFieldTemplateItems = {} endif state.mrssCustomFieldTemplateItems.AddReplace(elementName$, templateItem) endif else if name$ = "mrssImageTemplateItem" or name$ = "mrssMediaTemplateItem" then state.mrssMediaTemplateItem = newMRSSMediaTemplateItem(bsp, templateItems, templateItemXML) else if name$ = "customFields" then childElements = templateItemXML.GetChildElements() for each childElement in childElements newTemplateItem(bsp, state, state.templateItems, childElement) next 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.ClearTemplateItems = ClearTemplateItems state.RedisplayTemplateItems = RedisplayTemplateItems state.BuildTemplateItems = BuildTemplateItems state.BuildTemplateItem = BuildTemplateItem state.BuildTextTemplateItem = BuildTextTemplateItem state.TemplateUsesAnyUserVariable = TemplateUsesAnyUserVariable state.TemplateUsesUserVariable = TemplateUsesUserVariable state.TemplateUsesSystemVariable = TemplateUsesSystemVariable state.PlayVideo = PlayVideo state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents 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$, true ) bsp.liveDataFeeds.AddReplace(liveDataFeed.name$, liveDataFeed) endif state.liveDataFeed = liveDataFeed SetMRSSHandlers( state ) End Sub Sub newMRSSPlaylistItem(bsp As Object, zoneHSM 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$) if state.liveDataFeed <> invalid and lcase(state.liveDataFeed.usage$) = "mrsswith4k" then zoneHSM.useVideoPlayerForImages = true endif endif videoPlayerRequired$ = playlistItemXML.videoPlayerRequired.GetText() if len(videoPlayerRequired$) > 0 then videoPlayerRequired$ = lcase(videoPlayerRequired$) if videoPlayerRequired$ = "true" then zoneHSM.useVideoPlayerForImages = true endif 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.GetHtmlWidgetFilePath = GetHtmlWidgetFilePath state.DisplayImage = DisplayImage state.PreDrawImage = PreDrawImage state.DrawImage = DrawImage state.PostDrawImage = PostDrawImage state.ClearVideo = ClearVideo state.LaunchVideo = LaunchVideo state.PrePlayVideo = PrePlayVideo state.PlayVideo = PlayVideo state.PostPlayVideo = PostPlayVideo state.SetVideoTimeCodeEvents = SetVideoTimeCodeEvents state.GetAnyMediaRSSTransition = GetAnyMediaRSSTransition 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$ = "bp900DUserEvent" or userEventName$ = "bp200AUserEvent" or userEventName$ = "bp200BUserEvent" or userEventName$ = "bp200CUserEvent" or userEventName$ = "bp200DUserEvent" 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 port$ = userEvent.parameters.parameter.GetText() if bsp.usbDevicesByConnector.DoesExist(port$) then ' convert USB port if necessary port$ = bsp.usbDevicesByConnector.Lookup(port$) endif stateEvent.serialUserEventPort$ = port$ 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 else if userEventName$ = "synchronize" then stateEvent.synchronizeUserEvent$ = userEvent.parameters.parameter.GetText() bsp.IsSyncSlave = true endif next End Sub Function SetMediaListSynchronize(bsp As Object, navigation As Object, playlistItemXML As Object, brightSignCmds As String) navigation.sendSynchronizeCommand = false brightSignCmdsXML = playlistItemXML.GetNamedElements(brightSignCmds).brightSignCmd if type(brightSignCmdsXML) = "roXMLList" and brightSignCmdsXML.Count() > 0 then cmds = CreateObject("roArray", 1, true) for each brightSignCmdXML in brightSignCmdsXML newCmd(bsp, brightSignCmdXML, cmds) next bsp.IsSyncMaster = true navigation.sendSynchronizeCommand = true navigation.synchronizeKeyword = cmds[0].parameters["synchronizeKeyword"].getCurrentParameterValue() endif End Function 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()) if lcase(playlistItemXML.support4KImages.getText()) = "true" then zoneHSM.useVideoPlayerForImages = true endif if playlistItemXML.slideTransition.GetText() = "" then slideTransition$ = "No effect" else slideTransition$ = playlistItemXML.slideTransition.GetText() endif state.slideTransition% = GetSlideTransitionValue(slideTransition$) state.transitionDuration% = 1000 if playlistItemXML.transitionDuration.GetText() <> "" then state.transitionDuration% = int(val(playlistItemXML.transitionDuration.GetText())) endif state.sendZoneMessage = GetBoolFromXML(playlistItemXML.sendZoneMessage.GetText()) if playlistItemXML.startIndex.GetText() = "" then state.specifiedStartIndex% = 0 else state.specifiedStartIndex% = int(val(playlistItemXML.startIndex.GetText())) - 1 endif state.startIndex% = state.specifiedStartIndex% 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 mediaListStateInactivityTimeout = lcase(playlistItemXML.mediaListStateInactivityTimeout.GetText()) if mediaListStateInactivityTimeout = "true" then state.mediaListStateInactivityTimeout = true else state.mediaListStateInactivityTimeout = false endif mediaListStateInactivityTime = playlistItemXML.mediaListStateInactivityTime.GetText() if len(mediaListStateInactivityTime) > 0 then state.mediaListStateInactivityTime% = int(val(mediaListStateInactivityTime)) else state.mediaListStateInactivityTime% = 0 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 ' parse BrightSignCmdsTransitionNextItem state.transitionNextItemCmds = CreateObject("roArray", 1, true) transitionNextItemCmds = playlistItemXML.brightSignCmdsTransitionNextItem.brightSignCmd if transitionNextItemCmds.Count() > 0 then for each cmd in transitionNextItemCmds newCmd(bsp, cmd, state.transitionNextItemCmds) next endif ' parse BrightSignCmdsTransitionPrevItem state.transitionPrevItemCmds = CreateObject("roArray", 1, true) transitionPrevItemCmds = playlistItemXML.brightSignCmdsTransitionPrevItem.brightSignCmd if transitionPrevItemCmds.Count() > 0 then for each cmd in transitionPrevItemCmds newCmd(bsp, cmd, state.transitionPrevItemCmds) next endif if state.mediaType$ = "image" or state.mediaType$ = "allMedia" then zoneHSM.numImageItems% = zoneHSM.numImageItems% + 1 endif if state.populateFromMediaLibrary then childElements = playlistitemXML.files.getChildElements() state.numItems% = childElements.Count() state.items = CreateObject("roArray", state.numItems%, true) for each childElement in childElements item = {} if childElement.getName() = "videoItem" then newVideoPlaylistItem(bsp, childElement, state, item) item.type = "video" else if childElement.getName() = "imageItem" then newImagePlaylistItem(bsp, childElement, zoneHSM, state, item) item.type = "image" else if childElement.getName() = "audioItem" then newAudioPlaylistItem(bsp, childElement, state, item) item.type = "audio" endif state.items.push(item) next 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% = state.startIndex% 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.ClearVideo = ClearVideo state.StartInactivityTimer = StartInactivityTimer state.HandleIntraStateEvent = HandleIntraStateEvent state.LaunchMediaListPlaybackItem = LaunchMediaListPlaybackItem state.AdvanceMediaListPlayback = AdvanceMediaListPlayback state.RetreatMediaListPlayback = RetreatMediaListPlayback state.AddAudioTimeCodeEvent = AddAudioTimeCodeEvent state.SetAudioTimeCodeEvents = SetAudioTimeCodeEvents 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 state.AtEndOfMediaList = AtEndOfMediaList 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()) state.useDefaultMedia = false if lcase(playlistItemXML.useDefaultMedia.GetText()) = "true" then state.useDefaultMedia = true state.defaultMediaFileName$ = playlistItemXML.defaultMediaFileName.GetText() endif state.useUserVariable = false if lcase(playlistItemXML.useUserVariable.GetText()) = "true" then state.useUserVariable = true state.userVariable = bsp.GetUserVariable(playlistItemXML.userVariable.name.GetText()) if type(state.userVariable) <> "roAssociativeArray" then state.useUserVariable = false endif endif 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.automaticallyLoop = true fileTableEntry.isEncrypted = false 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.ClearVideo = ClearVideo 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.SetAudioTimeCodeEvents = SetAudioTimeCodeEvents 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.AddAudioTimeCodeEvent = AddAudioTimeCodeEvent state.SetAudioTimeCodeEvents = SetAudioTimeCodeEvents 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 newUserVariablePlaylistItem(bsp As Object, zoneHSM As Object, playlistItemXML As Object) As Object item = {} item.textStrings = [] item.userVariableName = playlistItemXML.userVariableName.GetText() userVariables = bsp.currentUserVariables for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.name$ = item.userVariableName then tickerItem = userVariable.GetCurrentValue() item.textStrings.push(tickerItem) endif next item.isRSSFeed = false item.isUserVariable = true return item End Function 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 item.isUserVariable = 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$ + "&tweet_mode=extended" 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 liveDataFeed.isTwitterFeed = 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 item.isUserVariable = false 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 item.isUserVariable = false 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 item.isUserVariable = false if isTwitterFeed then liveDataFeed.isJSON = true liveDataFeed.isTwitterFeed = true else liveDataFeed.isJSON = false liveDataFeed.isTwitterFeed = 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 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.usbAMinVolume% audioSettings.maxVolume% = m.sign.usbAMaxVolume% 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("usbA") then parameter = parameters["usbA"] usbA$ = parameter.GetCurrentParameterValue() else usbA$ = "none" endif if parameters.DoesExist("usbB") then parameter = parameters["usbB"] usbB$ = parameter.GetCurrentParameterValue() else usbB$ = "none" endif if parameters.DoesExist("usbC") then parameter = parameters["usbC"] usbC$ = parameter.GetCurrentParameterValue() else usbC$ = "none" endif if parameters.DoesExist("usbD") then parameter = parameters["usbD"] usbD$ = parameter.GetCurrentParameterValue() else usbD$ = "none" endif if parameters.DoesExist("usbA1") then parameter = parameters["usbA1"] usbA1$ = parameter.GetCurrentParameterValue() else usbA1$ = "none" endif if parameters.DoesExist("usbA2") then parameter = parameters["usbA2"] usbA2$ = parameter.GetCurrentParameterValue() else usbA2$ = "none" endif if parameters.DoesExist("usbA3") then parameter = parameters["usbA3"] usbA3$ = parameter.GetCurrentParameterValue() else usbA3$ = "none" endif if parameters.DoesExist("usbA4") then parameter = parameters["usbA4"] usbA4$ = parameter.GetCurrentParameterValue() else usbA4$ = "none" endif if parameters.DoesExist("usbA5") then parameter = parameters["usbA5"] usbA5$ = parameter.GetCurrentParameterValue() else usbA5$ = "none" endif if parameters.DoesExist("usbA6") then parameter = parameters["usbA6"] usbA6$ = parameter.GetCurrentParameterValue() else usbA6$ = "none" endif if parameters.DoesExist("usbA7") then parameter = parameters["usbA7"] usbA7$ = parameter.GetCurrentParameterValue() else usbA7$ = "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") if lcase(analog$) <> "none" and lcase(analog$) <> "multichannel" then pcm.push(analogAudioOutput) endif ' FIXME - if needed if lcase(analog2$) = "pcm" and type(analog2AudioOutput) = "roAudioOutput" 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 m.SetUSBAudioOutput("A", usbA$, pcm, multichannel) m.SetUSBAudioOutput("B", usbB$, pcm, multichannel) m.SetUSBAudioOutput("C", usbC$, pcm, multichannel) m.SetUSBAudioOutput("D", usbD$, pcm, multichannel) m.SetUSBAudioOutput("A/1", usbA1$, pcm, multichannel) m.SetUSBAudioOutput("A/2", usbA2$, pcm, multichannel) m.SetUSBAudioOutput("A/3", usbA3$, pcm, multichannel) m.SetUSBAudioOutput("A/4", usbA4$, pcm, multichannel) m.SetUSBAudioOutput("A/5", usbA5$, pcm, multichannel) m.SetUSBAudioOutput("A/6", usbA6$, pcm, multichannel) m.SetUSBAudioOutput("A/7", usbA7$, pcm, multichannel) 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 SetUSBAudioOutput(port$ As String, audioType$ As String, pcm As Object, multichannel As Object) if m.boseUSBAudioDevicesByConnector.DoesExist(port$) then usbPort$ = m.boseUSBAudioDevicesByConnector[port$] usbAudioOutput = CreateObject("roAudioOutput", usbPort$) if type(usbAudioOutput) = "roAudioOutput" then if lcase(audioType$) = "pcm" then pcm.push(usbAudioOutput) else if lcase(audioType$) = "multichannel" then multichannel.push(usbAudioOutput) endif endif endif End Sub Sub UnmuteAudioConnector(connector$ As String) audioOutput = CreateObject("roAudioOutput", connector$) if type(audioOutput) = "roAudioOutput" then audioOutput.SetMute(false) endif End Sub Function DeviceMatchesFid(boseProductPort As String, portName As String, fid As String, fidSubstring As String) As Boolean if boseProductPort = portName and instr(1, fid, fidSubstring) = 1 then return true endif return false End Function Function DeviceMatchesUSB700(boseProductPort As String, fid As String) As String if DeviceMatchesFid(boseProductPort, "USB 700_1", fid, "A/1") then return "USB A/1" else if DeviceMatchesFid(boseProductPort, "USB 700_2", fid, "A/2") then return "USB A/2" else if DeviceMatchesFid(boseProductPort, "USB 700_3", fid, "A/3") then return "USB A/3" else if DeviceMatchesFid(boseProductPort, "USB 700_4", fid, "A/4") then return "USB A/4" else if DeviceMatchesFid(boseProductPort, "USB 700_5", fid, "A/5") then return "USB A/5" else if DeviceMatchesFid(boseProductPort, "USB 700_6", fid, "A/6") then return "USB A/6" else if DeviceMatchesFid(boseProductPort, "USB 700_7", fid, "A/7") then return "USB A/7" endif return "" End Function Function GetConnectedUSBDeviceName(model As String, connectedUSBDevices As Object, boseProductPort As String) As String if lcase(model) = "hd972" then for each connectedUSBDevice in connectedUSBDevices fid = connectedUSBDevice.fid if DeviceMatchesFid(boseProductPort, "USB A", fid, "A") then return "USB A" else if DeviceMatchesFid(boseProductPort, "USB B", fid, "B") then return "USB B" else if DeviceMatchesFid(boseProductPort, "USB C", fid, "C") then return "USB C" else if DeviceMatchesFid(boseProductPort, "USB D", fid, "D") then return "USB D" endif next else if lcase(model) = "xt1143" or lcase(model) = "xd1033" or lcase(model) = "xt1144" or lcase(model) = "xd1034" then for each connectedUSBDevice in connectedUSBDevices fid = connectedUSBDevice.fid if DeviceMatchesFid(boseProductPort, "USB A", fid, "A") then return "USB A" else if DeviceMatchesFid(boseProductPort, "USB B", fid, "B") then return "USB B" else if DeviceMatchesFid(boseProductPort, "USB Type_A", fid, "B") then return "USB B" else if DeviceMatchesFid(boseProductPort, "USB Type_C", fid, "A") then return "USB A" else usbDeviceName$ = DeviceMatchesUSB700(boseProductPort, fid) if usbDeviceName$ <> "" then return usbDeviceName$ endif endif next else if lcase(model) = "ls423" or lcase(model) = "ls424" then for each connectedUSBDevice in connectedUSBDevices fid = connectedUSBDevice.fid ' does this make sense for the LS423/LS424 (there is no Type A port)? if DeviceMatchesFid(boseProductPort, "USB A", fid, "A") then return "USB A" else if DeviceMatchesFid(boseProductPort, "USB Type_C", fid, "A") then return "USB A" else usbDeviceName$ = DeviceMatchesUSB700(boseProductPort, fid) if usbDeviceName$ <> "" then return usbDeviceName$ endif endif next endif return "" End Function Function BuildUSBDevicesByConnector(sign As Object) As Object di = CreateObject("roDeviceInfo") connectedUSBDevices = di.GetUSBTopology({ array : true }) usbDevicesByConnector = {} usbBACommandToDeviceCommandMapping = {} usbConnectorToConnectorParameterMapping = {} for each connector in sign.boseProductsByConnector usbDeviceName = GetConnectedUSBDeviceName(di.GetModel(), connectedUSBDevices, connector) if usbDeviceName <> "" then usbDevicesByConnector.AddReplace(connector, usbDeviceName) ' Specify the following in a lookup table if lcase(connector) = "usb a" then connectorParameter = "usbA" else if lcase(connector) = "usb b" then connectorParameter = "usbB" else if lcase(connector) = "usb type_a" then connectorParameter = "usbTypeA" else if lcase(connector) = "usb type_c" then connectorParameter = "usbTypeC" else if lcase(connector) = "usb 700_1" then connectorParameter = "usb700_1" else if lcase(connector) = "usb 700_2" then connectorParameter = "usb700_2" else if lcase(connector) = "usb 700_3" then connectorParameter = "usb700_3" else if lcase(connector) = "usb 700_4" then connectorParameter = "usb700_4" else if lcase(connector) = "usb 700_5" then connectorParameter = "usb700_5" else if lcase(connector) = "usb 700_6" then connectorParameter = "usb700_6" else if lcase(connector) = "usb 700_7" then connectorParameter = "usb700_7" else stop endif usbConnectorToConnectorParameterMapping.AddReplace(lcase(connectorParameter), lcase(connector)) if lcase(usbDeviceName) = "usb a" then ' true for XT1143 usbDeviceNameParameter = "usbA" else if lcase(usbDeviceName) = "usb b" then ' true for XT1143 usbDeviceNameParameter = "usbB" else if lcase(usbDeviceName) = "usb a/1" usbDeviceNameParameter = "usbA/1" else if lcase(usbDeviceName) = "usb a/2" usbDeviceNameParameter = "usbA/2" else if lcase(usbDeviceName) = "usb a/3" usbDeviceNameParameter = "usbA/3" else if lcase(usbDeviceName) = "usb a/4" usbDeviceNameParameter = "usbA/4" else if lcase(usbDeviceName) = "usb a/5" usbDeviceNameParameter = "usbA/5" else if lcase(usbDeviceName) = "usb a/6" usbDeviceNameParameter = "usbA/6" else if lcase(usbDeviceName) = "usb a/7" usbDeviceNameParameter = "usbA/7" else stop endif usbBACommandToDeviceCommandMapping.AddReplace(connectorParameter, usbDeviceNameParameter) endif next usbMappings = {} usbMappings.usbDevicesByConnector = usbDevicesByConnector usbMappings.usbBACommandToDeviceCommandMapping = usbBACommandToDeviceCommandMapping usbMappings.usbConnectorToConnectorParameterMapping = usbConnectorToConnectorParameterMapping return usbMappings End Function Function BuildBoseUSBAudioDevicesByConnector(sign As Object) As Object boseUSBAudioDevicesByConnector = {} for each boseProductPort in sign.boseProductsByConnector if lcase(left(boseProductPort, 3)) = "usb" then usbPort$ = "USB:" + right(boseProductPort, len(boseProductPort) - 4) if GetGlobalAA().usbAudioPortConfigurations.DoesExist(boseProductPort) then usbAudioPortConfiguration = GetGlobalAA().usbAudioPortConfigurations[boseProductPort] usbPort$ = usbPort$ + "." + usbAudioPortConfiguration.usbAudioInterfaceIndex$ endif boseUSBAudioDevicesByConnector.AddReplace(right(boseProductPort, len(boseProductPort) - 4), usbPort$) endif next return boseUSBAudioDevicesByConnector End Function Sub UnmuteAllAudio() m.UnmuteAudioConnector("Analog:1") m.UnmuteAudioConnector("Analog:2") m.UnmuteAudioConnector("Analog:3") m.UnmuteAudioConnector("HDMI") m.UnmuteAudioConnector("SPDIF") for each boseUSBPort in m.boseUSBAudioDevicesByConnector usbPort$ = m.boseUSBAudioDevicesByConnector[boseUSBPort] m.UnmuteAudioConnector(usbPort$) next 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") for each portName in parameters if left(lcase(portName), 3) = "usb" and len(portName) >= 4 then port$ = right(portName, len(portName) - 3) if GetGlobalAA().usbParamNameToDeviceParamName.DoesExist(port$) then port$ = GetGlobalAA().usbParamNameToDeviceParamName[port$] endif if m.boseUSBAudioDevicesByConnector.DoesExist(port$) then deviceName$ = m.boseUSBAudioDevicesByConnector[port$] m.MuteAudioOutput(muteOn, parameters, portName, deviceName$) endif endif next 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 left(lcase(connector$), 3) = "usb" then port$ = right(connector$, len(connector$) - 4) if m.boseUSBAudioDevicesByConnector.DoesExist(port$) then deviceName$ = m.boseUSBAudioDevicesByConnector[port$] audioOutput = CreateObject("roAudioOutput", deviceName$) if port$ = "A" then m.usbVolumeA% = volume% else if port$ = "B" then m.usbVolumBA% = volume% else if port$ = "C" then m.usbVolumeC% = volume% else if port$ = "D" then m.usbVolumeD% = volume% else if port$ = "TypeA" then m.usbVolumeTypeA% = volume% else if port$ = "TypeC" then m.usbVolumeTypeC% = volume% else if port$ = "A/1" then m.usbVolumeA1% = volume% else if port$ = "A/2" then m.usbVolumeA2% = volume% else if port$ = "A/3" then m.usbVolumeA3% = volume% else if port$ = "A/4" then m.usbVolumeA4% = volume% else if port$ = "A/5" then m.usbVolumeA5% = volume% else if port$ = "A/6" then m.usbVolumeA6% = volume% else if port$ = "A/7" then m.usbVolumeA7% = volume% endif endif 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 left(lcase(connector$), 3) = "usb" then key$ = ucase(right(connector$, len(connector$) - 3)) if m.usbConnectorToConnectorParameterMapping.DoesExist("usb"+lcase(key$)) then fullUsbId = m.usbConnectorToConnectorParameterMapping.Lookup("usb"+lcase(key$)) if m.usbDevicesByConnector.DoesExist(fullUsbId) then if m.usbDevicesByConnector.DoesExist(fullUsbId) then usbDeviceId = m.usbDevicesByConnector.Lookup(fullUsbId) key$ = ucase(right(usbDeviceId, len(usbDeviceId) - 4)) endif endif endif if m.boseUSBAudioDevicesByConnector.DoesExist(key$) then usbPort$ = m.boseUSBAudioDevicesByConnector[key$] if key$ = "A" then m.usbVolumeA% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA% + volumeDelta%, m.sign.usbAMinVolume%, m.sign.usbAMaxVolume%) else if key$ = "B" then m.usbVolumeB% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeB% + volumeDelta%, m.sign.usbBMinVolume%, m.sign.usbBMaxVolume%) else if key$ = "C" then m.usbVolumeC% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeC% + volumeDelta%, m.sign.usbCMinVolume%, m.sign.usbCMaxVolume%) else if key$ = "D" then m.usbVolumeD% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeD% + volumeDelta%, m.sign.usbDMinVolume%, m.sign.usbDMaxVolume%) else if key$ = "A/1" then m.usbVolumeA1% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA1% + volumeDelta%, m.sign.usbA1MinVolume%, m.sign.usbA1MaxVolume%) else if key$ = "A/2" then m.usbVolumeA2% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA2% + volumeDelta%, m.sign.usbA2MinVolume%, m.sign.usbA2MaxVolume%) else if key$ = "A/3" then m.usbVolumeA3% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA3% + volumeDelta%, m.sign.usbA3MinVolume%, m.sign.usbA3MaxVolume%) else if key$ = "A/4" then m.usbVolumeA4% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA4% + volumeDelta%, m.sign.usbA4MinVolume%, m.sign.usbA4MaxVolume%) else if key$ = "A/5" then m.usbVolumeA5% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA5% + volumeDelta%, m.sign.usbA5MinVolume%, m.sign.usbA5MaxVolume%) else if key$ = "A/6" then m.usbVolumeA6% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA6% + volumeDelta%, m.sign.usbA6MinVolume%, m.sign.usbA6MaxVolume%) else if key$ = "A/7" then m.usbVolumeA7% = ExecuteChangeConnectorVolume(usbPort$, m.usbVolumeA7% + volumeDelta%, m.sign.usbA7MinVolume%, m.sign.usbA7MaxVolume%) endif endif endif End Sub Function ExecuteChangeConnectorVolume(connector$ As String, newVolume% As Integer, minVolume% As Integer, maxVolume% As Integer) As Integer audioOutput = CreateObject("roAudioOutput", connector$) if type(audioOutput) = "roAudioOutput" then if newVolume% > maxVolume% then newVolume% = maxVolume% else if newVolume% < minVolume% newVolume% = minVolume% endif audioOutput.SetVolume(newVolume%) endif 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, videoPlayerRequired 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 videoPlayerRequired 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 type(m.videoPlayer) = "roVideoPlayer" then m.videoPlayer.ConfigureAudioResources() else if IsAudioPlayer(m.audioPlayer) then m.audioPlayer.ConfigureAudioResources() endif End Sub Sub CecDisplayOn() m.SendCecCommand("400D", "true") End Sub Sub CecDisplayOff() m.SendCecCommand("4036", "true") End Sub Sub CecSetSourceBrightSign() m.SendCecCommand("4F821000", "false") End Sub Sub CecPhilipsSetVolume(volume% As Integer) b = CreateObject("roByteArray") b[0] = volume% volumeAsAscii$ = b.ToHexString() b = invalid setVolume$ = "40A0000C3022" + volumeAsAscii$ SendCecCommand(setVolume$, "true") End Sub Sub SendCecCommand(cecCommand$ As String, cecSubstituteSourceAddress$ As String) cec = CreateObject("roCecInterface") if type(cec) = "roCecInterface" then if m.useInitiatorAddressFromPacketSupported and lcase(cecSubstituteSourceAddress$) = "false" then cec.UseInitiatorAddressFromPacket(true) endif 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 Function DeviceSupportsRotation() As Boolean if m.sysInfo.deviceFamily$ = "panther" or m.sysInfo.deviceFamily$ = "puma" then return false endif return true 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() ' scale the zones if necessary zoneHSM.originalWidth% = int(val(zoneXML.width.GetText())) zoneHSM.originalHeight% = int(val(zoneXML.height.GetText())) scaleScreenElement(bsp, true, zoneHSM, zoneXML) zoneHSM.type$ = zoneXML.type.GetText() zoneHSM.id$ = zoneXML.id.GetText() zoneHSM.isVisible = true zoneHSM.imageHidden = false zoneHSM.canvasHidden = false zoneHSM.htmlHidden = false zoneHSM.mosaicDecoderName = "" 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", 8, true) for i% = 0 to 7 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$ = "NEQ" 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 nextState.useUserVariable then userVariable = nextState.userVariable payload$ = userVariable.GetCurrentValue() endif if not nextState.useDefaultMedia and 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 if transition.remainOnCurrentStateActions = "stop" then if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.Stop() endif else if transition.remainOnCurrentStateActions = "stopclear" then if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.StopDisplay() endif endif 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 IsControlPort(m.bsp.controlPort) 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 IsControlPort(m.bsp.controlPort) 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% = mediaListState.startIndex% next else m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "mediaListInactivityTimer", "", "0") endif return "HANDLED" endif endif endif userData = event.GetUserData() if type(userData) = "roAssociativeArray" then if IsString(userData.id) then if userData.id = "mediaList" then mediaListState = userData.state mediaListState.playbackIndex% = mediaListState.startIndex% 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) = "roStreamEndEvent" then ' Bose port? port$ = event.GetUserData() m.bsp.diagnostics.PrintDebug("roStreamEndEvent received on port " + port$) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "streamEnd", port$, "0") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_STREAM_END, port$) outputOnly = false ' WHAT IS THE PURPOSE OF THIS CODE? if type(m.bsp.serial) = "roAssociativeArray" and m.bsp.serial.DoesExist(port$) then m.bsp.serial[port$] = invalid endif if type(m.bsp.serialOutputOnlySpec) = "roAssociativeArray" and m.bsp.serialOutputOnlySpec.DoesExist(port$) then outputOnly = m.bsp.serialOutputOnlySpec[port$] endif m.bsp.ScheduleRetryCreateSerial(port$, outputOnly) else if type(event) = "roStreamLineEvent" then serialEvent$ = event.GetString() port$ = event.GetUserData() if not IsUsbCommunicationPort(port$) then 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 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) = "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, "") else ' look for regular expression match with each of the possible zone message events for the current state for each sendZoneMessageSpec in m.zoneMessageEvents ' only look for regular expressions if spec includes wildcard if instr(1, sendZoneMessageSpec, "(.*)") > 0 then r = CreateObject("roRegEx", sendZoneMessageSpec, "i") if type(r) = "roRegex" then matches = r.match(sendZoneMessageParameter$) if matches.Count() > 0 then transition = m.zoneMessageEvents[sendZoneMessageSpec] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendZoneMessage", sendZoneMessageParameter$, "1") if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, sendZoneMessageParameter$) endif if matches.Count() > 1 and transition.assignWildcardToUserVariable then transition.AssignWildcardInputToUserVariable(m.bsp, matches[1]) endif return m.ExecuteTransition(transition, stateData, sendZoneMessageParameter$) endif endif endif next 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, "") else for each pluginMessageEvent in m.pluginMessageEvents if instr(1, pluginMessageEvent, "(.*)") > 0 then r = CreateObject("roRegEx", pluginMessageEvent, "i") if type(r) = "roRegex" then key$ = pluginName$ + pluginMessage$ matches = r.match(key$) if matches.Count() > 0 then transition = m.pluginMessageEvents[pluginMessageEvent] m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "sendPluginMessage", key$, "1") if transition.assignInputToUserVariable then transition.AssignEventInputToUserVariable(m.bsp, event.PluginMessage) endif if matches.Count() > 1 and transition.assignWildcardToUserVariable then transition.AssignWildcardInputToUserVariable(m.bsp, matches[1]) endif return m.ExecuteTransition(transition, stateData, event.PluginMessage) endif endif endif next 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") m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeCode", "", "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 imageItem.isEncrypted = false else if fileType$ = "video" then videoItem = {} videoItem.fileName$ = fileName$ videoItem.probeData = fileTableEntry.probeData videoItem.isEncrypted = false 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$) isEncrypted = m.bsp.encryptionByFile.DoesExist(imageItem.fileName$) 'isEncrypted = m.bsp.contentEncrypted if isEncrypted then aa = { } aa.AddReplace("Filename", imageItemFilePath$) aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", imageItem.fileName$) zone.imagePlayer.PreloadFile(aa) else zone.imagePlayer.PreloadFile(imageItemFilePath$) endif 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 isEncrypted = m.bsp.encryptionByFile.DoesExist(videoItem.fileName$) 'isEncrypted = m.bsp.contentEncrypted if isEncrypted then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", videoItem.fileName$) 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) if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then imagePlayer.SetTransform("rot90") else if m.bsp.sign.monitorOrientation = "portraitbottomonright" then imagePlayer.SetTransform("rot270") endif endif 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.usbOutputA$ = zoneXML.zoneSpecificParameters.usbOutputA.GetText() zoneHSM.usbOutputB$ = zoneXML.zoneSpecificParameters.usbOutputB.GetText() zoneHSM.usbOutputC$ = zoneXML.zoneSpecificParameters.usbOutputC.GetText() zoneHSM.usbOutputD$ = zoneXML.zoneSpecificParameters.usbOutputD.GetText() zoneHSM.usbOutputTypeA$ = zoneXML.zoneSpecificParameters.usbOutputTypeA.GetText() zoneHSM.usbOutputTypeC$ = zoneXML.zoneSpecificParameters.usbOutputTypeC.GetText() zoneHSM.usbOutputA1$ = zoneXML.zoneSpecificParameters.usbOutput700_1.GetText() zoneHSM.usbOutputA2$ = zoneXML.zoneSpecificParameters.usbOutput700_2.GetText() zoneHSM.usbOutputA3$ = zoneXML.zoneSpecificParameters.usbOutput700_3.GetText() zoneHSM.usbOutputA4$ = zoneXML.zoneSpecificParameters.usbOutput700_4.GetText() zoneHSM.usbOutputA5$ = zoneXML.zoneSpecificParameters.usbOutput700_5.GetText() zoneHSM.usbOutputA6$ = zoneXML.zoneSpecificParameters.usbOutput700_6.GetText() zoneHSM.usbOutputA7$ = zoneXML.zoneSpecificParameters.usbOutput700_7.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 audioInput = CreateObject("roAudioInput", m.bsp.sign.audioInSampleRate%) if type(audioInput) = "roAudioInput" then zoneHSM.audioInput = audioInput 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.usbOutputA$ = zoneXML.zoneSpecificParameters.usbOutputA.GetText() zoneHSM.usbOutputB$ = zoneXML.zoneSpecificParameters.usbOutputB.GetText() zoneHSM.usbOutputC$ = zoneXML.zoneSpecificParameters.usbOutputC.GetText() zoneHSM.usbOutputD$ = zoneXML.zoneSpecificParameters.usbOutputD.GetText() zoneHSM.usbOutputTypeA$ = zoneXML.zoneSpecificParameters.usbOutputTypeA.GetText() zoneHSM.usbOutputTypeC$ = zoneXML.zoneSpecificParameters.usbOutputTypeC.GetText() zoneHSM.usbOutputA1$ = zoneXML.zoneSpecificParameters.usbOutput700_1.GetText() zoneHSM.usbOutputA2$ = zoneXML.zoneSpecificParameters.usbOutput700_2.GetText() zoneHSM.usbOutputA3$ = zoneXML.zoneSpecificParameters.usbOutput700_3.GetText() zoneHSM.usbOutputA4$ = zoneXML.zoneSpecificParameters.usbOutput700_4.GetText() zoneHSM.usbOutputA5$ = zoneXML.zoneSpecificParameters.usbOutput700_5.GetText() zoneHSM.usbOutputA6$ = zoneXML.zoneSpecificParameters.usbOutput700_6.GetText() zoneHSM.usbOutputA7$ = zoneXML.zoneSpecificParameters.usbOutput700_7.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 zoneHSM.mosaicDecoderName = zoneXML.zoneSpecificParameters.mosaicDecoderName.GetText() return zoneHSM End Function ' use roAudioOutput if all of the following are true ' 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.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") 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 gaa = GetGlobalAA() for each boseUSBPort in m.bsp.boseUSBAudioDevicesByConnector usbPort$ = m.bsp.boseUSBAudioDevicesByConnector[boseUSBPort] usbAudioOutput = CreateObject("roAudioOutput", usbPort$) ' convert boseUSBPort to usb friendly name usbDeviceName = "USB " + boseUSBPort for each key in gaa.usbDevicesByConnector if usbDeviceName = gaa.usbDevicesByConnector[key] then usbFriendlyName = key endif next if usbFriendlyName = "USB A" then spec$ = m.usbOutputA$ else if usbFriendlyName = "USB B" then spec$ = m.usbOutputB$ else if usbFriendlyName = "USB C" then spec$ = m.usbOutputC$ else if usbFriendlyName = "USB D" then spec$ = m.usbOutputD$ else if usbFriendlyName = "USB Type_A" then spec$ = m.usbOutputTypeA$ else if usbFriendlyName = "USB Type_C" then spec$ = m.usbOutputTypeC$ else if usbFriendlyName = "USB 700_1" then spec$ = m.usbOutputA1$ else if usbFriendlyName = "USB 700_2" then spec$ = m.usbOutputA2$ else if usbFriendlyName = "USB 700_3" then spec$ = m.usbOutputA3$ else if usbFriendlyName = "USB 700_4" then spec$ = m.usbOutputA4$ else if usbFriendlyName = "USB 700_5" then spec$ = m.usbOutputA5$ else if usbFriendlyName = "USB 700_6" then spec$ = m.usbOutputA6$ else if usbFriendlyName = "USB 700_7" then spec$ = m.usbOutputA7$ endif if type(usbAudioOutput) = "roAudioOutput" then if lcase(spec$) = "pcm" then pcm.push(usbAudioOutput) else if lcase(spec$) = "multichannel" then multichannel.push(usbAudioOutput) endif endif next 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 if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then videoPlayer.SetTransform("rot90") else if m.bsp.sign.monitorOrientation = "portraitbottomonright" then videoPlayer.SetTransform("rot270") endif endif 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() ' refactor to not puma, not panther if m.bsp.sysInfo.deviceFamily$ = "cheetah" or m.bsp.sysInfo.deviceFamily$ = "tiger" or m.bsp.sysInfo.deviceFamily$ = "lynx" or m.bsp.sysInfo.deviceFamily$ = "impala" or m.bsp.sysInfo.deviceFamily$ = "malibu" or m.bsp.sysInfo.deviceFamily$ = "pantera" 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%) if m.stateMachine.mosaicDecoderName <> "" then aa.Decoder = m.stateMachine.mosaicDecoderName endif 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") m.stateMachine.mjpegUrl.SetUserAgent(m.bsp.userAgent$) 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") if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then m.stateMachine.mjpegVideoPlayer.SetTransform("rot90") else if m.bsp.sign.monitorOrientation = "portraitbottomonright" then m.stateMachine.mjpegVideoPlayer.SetTransform("rot270") endif endif 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 if m.stateMachine.mosaicDecoderName <> "" then aa.Decoder = m.stateMachine.mosaicDecoderName 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 SetAudioTimeCodeEvents() if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then player = m.stateMachine.audioPlayer else player = m.stateMachine.videoPlayer endif player.ClearEvents() if type(m.audioTimeCodeEvents) = "roAssociativeArray" then for each eventNum in m.audioTimeCodeEvents m.AddAudioTimeCodeEvent(m.audioTimeCodeEvents[eventNum].timeInMS%, int(val(eventNum))) next endif End Sub Sub AddAudioTimeCodeEvent(timeInMS% As Integer, eventNum% As Integer) if type(m.stateMachine.audioPlayer) = "roAudioPlayer" then m.stateMachine.audioPlayer.AddEvent(eventNum%, timeInMS%) else m.stateMachine.videoPlayer.AddEvent(eventNum%, timeInMS%) endif 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 if type(m.bsp.encryptionByFile) = "roAssociativeArray" and m.bsp.encryptionByFile.DoesExist(fileName$) and m.bsp.contentEncryptionSupported then 'if m.bsp.contentEncrypted then aa = {} aa.filename = filePath$ aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", fileName$) imageBuffer = CreateObject("roImageBuffer", 0, aa) else imageBuffer = CreateObject("roImageBuffer", 0, filePath$) endif 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) if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then imagePlayer.SetTransform("rot90") else if m.bsp.sign.monitorOrientation = "portraitbottomonright" then imagePlayer.SetTransform("rot270") endif endif 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 m.filesTable = {} 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.automaticallyLoop = true fileTableEntry.isEncrypted = false 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.useUserVariable then userVariable = m.userVariable m.payload$ = userVariable.GetCurrentValue() endif if m.filesTable.IsEmpty() or (not m.useDefaultMedia and 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$) if type(fileTableEntry) = "roAssociativeArray" then fileName$ = fileTableEntry.fileName$ fileType$ = fileTableEntry.fileType$ else fileName$ = m.defaultMediaFileName$ fileType$ = m.mediaType$ endif m.imageItem = invalid m.videoItem = invalid m.audioItem = invalid if fileType$ = "image" then m.imageItem = CreateObject("roAssociativeArray") m.imageItem.fileName$ = fileName$ m.imageItem.isEncrypted = m.bsp.encryptionByFile.DoesExist(fileName$) if type(fileTableEntry) = "roAssociativeArray" then if type(fileTableEntry.filePath$) = "roString" then m.imageItem.filePath$ = fileTableEntry.filePath$ endif m.imageItem.userVariable = fileTableEntry.userVariable endif m.imageItem.slideTransition% = m.slideTransition% m.imageItem.useImageBuffer = false m.DisplayImage("playFile") else if fileType$ = "video" then m.videoItem = CreateObject("roAssociativeArray") m.videoItem.fileName$ = fileName$ m.videoItem.isEncrypted = m.bsp.encryptionByFile.DoesExist(fileName$) if type(fileTableEntry) = "roAssociativeArray" then 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.videoItem.automaticallyLoop = fileTableEntry.automaticallyLoop else m.videoItem.videoDisplayMode% = 0 endif m.LaunchVideo("playFile") else if fileType$ = "audio" then m.audioItem = CreateObject("roAssociativeArray") m.audioItem.fileName$ = fileName$ m.audioItem.isEncrypted = m.bsp.encryptionByFile.DoesExist(fileName$) if type(fileTableEntry) = "roAssociativeArray" then if type(fileTableEntry.filePath$) = "roString" then m.audioItem.filePath$ = fileTableEntry.filePath$ endif m.audioItem.probeData = fileTableEntry.probeData m.audioItem.userVariable = fileTableEntry.userVariable endif 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.itemUrls.Count() m.items = CreateObject("roArray", m.numItems%, true) for i% = 0 to m.liveDataFeed.itemUrls.Count() - 1 url = m.liveDataFeed.itemUrls[i%] fileName$ = m.liveDataFeed.fileKeys[i%] filePath$ = m.liveDataFeed.assetPoolFiles.GetPoolFilePath(url) item = {} item.fileName$ = fileName$ item.filePath$ = filePath$ item.isEncrypted = false if m.mediaType$ = "image" then item.slideTransition% = 0 item.useImageBuffer = false item.type = "image" else if m.mediaType$ = "video" then item.videoDisplayMode% = 0 item.type = "video" else if m.mediaType$ = "audio" then item.type = "audio" else if m.mediaType$ = "allMedia" then if type(m.liveDataFeed.fileTypes) = "roArray" and m.liveDataFeed.fileTypes.Count() > i% then item.type = m.liveDataFeed.fileTypes[i%] endif if item.type = "image" then item.slideTransition% = 0 item.useImageBuffer = false else if item.type = "video" then item.videoDisplayMode% = 0 endif 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 m.bsp.mediaListInactivityTimeoutIsGlobal and type(m.bsp.mediaListInactivity) = "roAssociativeArray" then if type(m.bsp.mediaListInactivity.timer) = "roTimer" then m.bsp.mediaListInactivity.timer.Stop() else if m.bsp.inactivityTimeout then m.bsp.mediaListInactivity.timer = CreateObject("roTimer") m.bsp.mediaListInactivity.timer.SetPort(m.bsp.msgPort) endif endif if m.mediaListStateInactivityTimeout then m.mediaListInactivityTimer = CreateObject("roTimer") m.mediaListInactivityTimer.SetPort(m.bsp.msgPort) endif m.ConfigureIntraStateEventHandlerButton(m.nextNavigation) m.ConfigureIntraStateEventHandlerButton(m.previousNavigation) m.firstItemDisplayed = false ' prevent start index from pointing beyond the number of items in the case where m.playFromBeginning is false if m.numItems% > 0 and m.startIndex% >= m.numItems% then m.startIndex% = 0 endif ' reset playback index if appropriate if m.playFromBeginning then m.playbackIndex% = m.startIndex% endif if m.numItems% > 0 then m.playbackActive = true ' prevent start index from pointing beyond the number of items if m.playFromBeginning then if m.specifiedStartIndex% >= m.numItems% then m.startIndex% = 0 else m.startIndex% = m.specifiedStartIndex% endif m.playbackIndex% = m.startIndex% endif ' reshuffle media list if appropriate if m.playbackIndex% = m.startIndex% and m.shuffle then m.ShuffleMediaListContent() endif m.AdvanceMediaListPlayback(true, false) else m.playbackActive = false emptyListPlaybackEvent = CreateObject("roAssociativeArray") emptyListPlaybackEvent["EventType"] = "EmptyListPlaybackEvent" m.stateMachine.msgPort.PostMessage(emptyListPlaybackEvent) endif return "HANDLED" else if event["EventType"] = "EmptyListPlaybackEvent" then if type(m.mediaListEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.mediaListEndEvent, stateData, "") endif 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.PopulateMediaListFromLiveDataFeed() 'reset the playback index to the start point if m.specifiedStartIndex% >= m.numItems% then m.startIndex% = 0 else m.startIndex% = m.specifiedStartIndex% endif m.playbackIndex% = m.startIndex% if m.numItems% > 0 then if m.shuffle then m.ShuffleMediaListContent() endif if not m.playbackActive then m.playbackActive = true m.AdvanceMediaListPlayback(true, false) endif 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 ' detect whether or not this is an event that indicates that the media list has completed a loop - if yes, act on it if there is a mediaListEnd event ' test with media end event on media list first else if m.AtEndOfMediaList(event) and type(m.mediaListEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.mediaListEndEvent, stateData, "") else if (m.mediaType$ = "video" or m.mediaType$ = "allMedia") 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% = m.startIndex% and type(m.videoEndEvent) = "roAssociativeArray") then m.AdvanceMediaListPlayback(true, 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" or m.mediaType$ = "allMedia") 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%]] 'send zone message for the current track if m.sendZoneMessage then item = m.items[m.playbackIndices[currentTrackIndex%]] fileNameWithoutExtension$ = item.filename$ 'if the file name has an extension, remove it before sending ext=GetFileExtension(item.filename$) if type(ext) = "roString" then index=instr(1, item.filename$, ext) if index > 2 then fileNameWithoutExtension$ = mid(item.filename$, 1, index-2) endif endif ' send ZoneMessage using the file name as the message zoneMessageCmd = CreateObject("roAssociativeArray") zoneMessageCmd["EventType"] = "SEND_ZONE_MESSAGE" zoneMessageCmd["EventParameter"] = fileNameWithoutExtension$ m.bsp.msgPort.PostMessage(zoneMessageCmd) endif if not(m.playbackIndex% = m.startIndex% 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" or m.mediaType$ = "allMedia") 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% = m.startIndex% and type(m.audioEndEvent) = "roAssociativeArray") then m.AdvanceMediaListPlayback(true, 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% <> m.startIndex% or type(m.mstimeoutEvent) <> "roAssociativeArray" then m.AdvanceMediaListPlayback(true, true) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "timeout", "", "1") return "HANDLED" endif endif if m.playbackIndex% = m.startIndex% 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 if type(m.advanceOnImageTimeoutTimer) = "roTimer" then m.advanceOnImageTimeoutTimer.Stop() m.advanceOnImageTimeoutTimer = invalid endif m.AdvanceMediaListPlayback(true, true) return "HANDLED" endif endif if type(m.previousNavigation) = "roAssociativeArray" and m.playbackActive then retreat = m.HandleIntraStateEvent(event, m.previousNavigation) if retreat then if type(m.advanceOnImageTimeoutTimer) = "roTimer" then m.advanceOnImageTimeoutTimer.Stop() m.advanceOnImageTimeoutTimer = invalid endif m.RetreatMediaListPlayback(true, true) return "HANDLED" endif endif return m.MediaItemEventHandler(event, stateData) End Function Function AtEndOfMediaList(event As Object) As Boolean MEDIA_END = 8 endOfMediaEvent = false if (m.mediaType$ = "video" or m.mediaType$ = "allMedia") and type(event) = "roVideoEvent" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() and event.GetInt() = MEDIA_END then endOfMediaEvent = true else if (m.mediaType$ = "audio" or m.mediaType$ = "allMedia") and m.stateMachine.type$ = "EnhancedAudio" and type(event) = "roAudioEventMx" and event.GetInt() = MEDIA_END then endOfMediaEvent = true else if (m.mediaType$ = "audio" or m.mediaType$ = "allMedia") and m.stateMachine.type$ <> "EnhancedAudio" and IsAudioEvent(m.stateMachine, event) and event.GetInt() = MEDIA_END then endOfMediaEvent = true else if type(event) = "roTimerEvent" and m.advanceOnImageTimeout and type(m.advanceOnImageTimeoutTimer) = "roTimer" and event.GetSourceIdentity() = m.advanceOnImageTimeoutTimer.GetIdentity() and (m.playbackIndex% <> m.startIndex% or type(m.mstimeoutEvent) <> "roAssociativeArray") then endOfMediaEvent = true endif if endOfMediaEvent and m.playbackIndex% = 0 then return true else return false endif 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 IsControlPort(m.bsp.controlPort) 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) = "roSyncManagerEvent" and IsString(navigation.synchronizeUserEvent$) then synchronizeEvent$ = event.GetId() m.bsp.diagnostics.PrintDebug("Synchronize Event" + synchronizeEvent$) if navigation.synchronizeUserEvent$ = synchronizeEvent$ 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") 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, executeNextCommands As Boolean, executePrevCommands As Boolean) ' Make sure we have a valid list itemIndex = m.playbackIndices[m.playbackIndex%] if itemIndex = invalid then ' Attempting to play an empty list - log the failure m.bsp.diagnostics.PrintDebug("LaunchMediaListPlaybackItem failed - attempted to play an empty " + m.mediaType$ + " list") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_EMPTY_MEDIA_PLAYLIST, m.mediaType$) emptyListPlaybackEvent = CreateObject("roAssociativeArray") emptyListPlaybackEvent["EventType"] = "EmptyListPlaybackEvent" m.stateMachine.msgPort.PostMessage(emptyListPlaybackEvent) return endif ' get current media item and launch playback item = m.items[itemIndex] if m.sendZoneMessage and not((m.mediaType$ = "audio" or (m.mediaType$ = "allMedia" and item.type = "audio")) and m.statemachine.type$ = "EnhancedAudio") then m.bsp.diagnostics.PrintDebug("DEBUG: LaunchMediaListPlaybackItem sending Zone message") fileNameWithoutExtension$ = item.filename$ ' if the file name has an extension, remove it before sending ext=GetFileExtension(item.filename$) if type(ext) = "roString" then index=instr(1, item.filename$, ext) if index > 2 then fileNameWithoutExtension$ = mid(item.filename$, 1, index-2) endif endif ' send ZoneMessage using the file name as the message zoneMessageCmd = CreateObject("roAssociativeArray") zoneMessageCmd["EventType"] = "SEND_ZONE_MESSAGE" zoneMessageCmd["EventParameter"] = fileNameWithoutExtension$ m.bsp.msgPort.PostMessage(zoneMessageCmd) endif if executeNextCommands then if type(m.transitionNextItemCmds) = "roArray" then for each cmd in m.transitionNextItemCmds m.bsp.ExecuteCmd(m.stateMachine, cmd.name$, cmd.parameters) next endif endif if executePrevCommands then if type(m.transitionPrevItemCmds) = "roArray" then for each cmd in m.transitionPrevItemCmds m.bsp.ExecuteCmd(m.stateMachine, cmd.name$, cmd.parameters) next endif endif if m.mediaType$ = "image" or (m.mediaType$ = "allMedia" and item.type = "image") then m.imageItem = item m.imageItem.slideTransition% = m.slideTransition% m.imageItem.transitionDuration% = m.transitionDuration% if not m.firstItemDisplayed then m.PreDrawImage() endif m.DrawImage(true) if not m.firstItemDisplayed then m.PostDrawImage("imageList") else m.ClearVideo() 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" or (m.mediaType$ = "allMedia" and item.type = "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") else m.stateMachine.ClearImagePlane() endif else if m.mediaType$ = "audio" or (m.mediaType$ = "allMedia" and item.type = "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, true) endif if not m.firstItemDisplayed then m.PostPlayAudio("audioList") endif endif m.firstItemDisplayed = true End Sub Sub AdvanceMediaListPlayback(playImmediate As Boolean, executeNextCommands As Boolean) m.LaunchMediaListPlaybackItem(playImmediate, executeNextCommands, false) m.playbackIndex% = m.playbackIndex% + 1 if m.playbackIndex% >= m.numItems% then m.playbackIndex% = 0 endif End Sub Sub RetreatMediaListPlayback(playImmediate As Boolean, executePrevCommands 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, false, executePrevCommands) m.playbackIndex% = m.playbackIndex% + 1 if m.playbackIndex% >= m.numItems% then m.playbackIndex% = 0 endif End Sub Sub StartInactivityTimer() if m.bsp.mediaListInactivityTimeoutIsGlobal and 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 else if m.mediaListStateInactivityTimeout then if type(m.mediaListInactivityTimer) = "roTimer" then userData = {} userData.id = "mediaList" userData.state = m m.mediaListInactivityTimer.SetUserData(userData) m.mediaListInactivityTimer.SetElapsed(m.mediaListStateInactivityTime%, 0) m.mediaListInactivityTimer.Start() endif endif End Sub Sub ConfigureBPButtons() for buttonPanelIndex% = 0 to 3 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 Function ItemIsEncrypted(item As Object) As Boolean if type(item.IsEncrypted) = "roBoolean" and item.isEncrypted then return true else return false endif End Function 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.videoItem.automaticallyLoop) = "roBoolean" and (not m.videoItem.automaticallyLoop)) or type(m.videoEndEvent) = "roAssociativeArray" or type(m.synchronizeEvents) = "roAssociativeArray" or type(m.internalSynchronizeEvents) = "roAssociativeArray" then loopMode% = 0 m.stateMachine.videoPlayer.SetLoopMode(loopMode%) file$ = m.videoItem.fileName$ if type(m.videoItem.filePath$) = "roString" then filePath$ = m.videoItem.filePath$ else 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 then m.bsp.diagnostics.PrintDebug("Error playing preloaded file in PlayVideo: " + 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 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 m.bsp.diagnostics.PrintDebug("LaunchVideo: probeData = " + m.videoItem.probeData) aa.AddReplace("ProbeString", m.videoItem.probeData) 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 if ItemIsEncrypted(m.videoItem) then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif if m.stateMachine.mosaicDecoderName <> "" then aa.Decoder = m.stateMachine.mosaicDecoderName 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 ) file$ = m.imageItem.fileName$ if type(m.imageItem.filePath$) = "roString" then filePath$ = m.imageItem.filePath$ else filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, file$) endif if m.stateMachine.useVideoPlayerForImages then m.bsp.diagnostics.PrintDebug("PlayStaticImage in DisplayImage: " + file$) if type(m.stateMachine.videoPlayer) <> "roVideoPlayer" then m.bsp.diagnostics.PrintDebug("DisplayImage attempted to call PlayStaticImage without a videoPlayer: " + file$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, "PlayStaticImage with no videoPlayer: " + file$) else aa = { } aa.AddReplace("Filename", filePath$) if ItemIsEncrypted(m.imageItem) then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif if m.bsp.contentEncryptionSupported then ok = m.stateMachine.videoPlayer.PlayStaticImage(aa) else ok = m.stateMachine.videoPlayer.PlayStaticImage(filePath$) endif if not ok then m.bsp.diagnostics.PrintDebug("Error returned from PlayStaticImage in DisplayImage: " + file$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) endif if type(m.stateMachine.imagePlayer) = "roImageWidget" then ok = m.stateMachine.imagePlayer.StopDisplay() endif endif else if setTransition then m.stateMachine.imagePlayer.SetDefaultTransition(m.imageItem.slideTransition%) endif if type(m.imageItem.transitionDuration%) = "roInt" then m.stateMachine.imagePlayer.SetTransitionDuration(m.imageItem.transitionDuration%) 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 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: ") 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 if not preloaded then aa = {} aa.filename = filePath$ if ItemIsEncrypted(m.imageItem) then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif ok = m.stateMachine.imagePlayer.DisplayFile(aa) if not ok then m.bsp.diagnostics.PrintDebug("Error displaying file in DisplayImage: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) else m.bsp.diagnostics.PrintDebug("Displayed file in DisplayImage: " + file$) endif endif 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) m.ClearVideo() m.LaunchTimer() m.bsp.SetTouchRegions(m) ' state logging m.bsp.logging.WriteStateLogEntry(m.stateMachine, m.id$, stateType$) End Sub Sub ClearVideo() ' this shouldn't be done for 4K image playback but must be done for other images if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then if not m.stateMachine.useVideoPlayerForImages then m.stateMachine.videoPlayer.StopClear() endif endif 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, disableLoopMode As Boolean) loopMode% = 1 if disableLoopMode or 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() m.SetAudioTimeCodeEvents() if executeEntryCmds then m.bsp.ExecuteMediaStateCommands(m.stateMachine, m.cmds) endif if type(m.audioItem.fileName$) = "roString" and len(m.audioItem.fileName$) > 0 then file$ = m.audioItem.fileName$ else file$ = m.audioItem.filePath$ endif if type(m.audioItem.filePath$) = "roString" then filePath$ = m.audioItem.filePath$ else filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, m.audioItem.fileName$) 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, false) 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$ = "" if type(m.stateMachine.audioPlayer) = "roVideoPlayer" then videoZone = m.bsp.GetVideoZone(m.stateMachine) if type(videoZone) = "roAssociativeArray" then videoZone.videoPlayer.StopClear() endif 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 aa = {} aa.filename = filePath$ if m.bsp.encryptionByFile.DoesExist(file$) 'if m.bsp.contentEncrypted then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif ok = m.stateMachine.imagePlayer.DisplayFile(aa) if not ok then m.bsp.diagnostics.PrintDebug("Error displaying file in STAudioInPlayingEventHandler: " + file$ + ", " + filePath$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_PLAYBACK_FAILURE, file$) else m.bsp.diagnostics.PrintDebug("Displayed file in STAudioInPlayingEventHandler: " + file$) 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 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.Stop() m.stateMachine.ClearImagePlane() if m.bsp.sysInfo.deviceFamily$ = "cheetah" or m.bsp.sysInfo.deviceFamily$ = "tiger" or m.bsp.sysInfo.deviceFamily$ = "lynx" or m.bsp.sysInfo.deviceFamily$ = "impala" or m.bsp.sysInfo.deviceFamily$ = "malibu" or m.bsp.sysInfo.deviceFamily$ = "pantera" 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 if m.stateMachine.mosaicDecoderName <> "" then aa.Decoder = m.stateMachine.mosaicDecoderName 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 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, "") endif PostMediaEndEvent(m.bsp.msgPort)' endif else return m.MediaItemEventHandler(event, stateData) endif stateData.nextState = m.superState return "SUPER" End Function Sub SetUserAgentForHtmlWidget(bsp as Object, htmlWidget as Object) if bsp.httpWidgetGetUserAgentSupported and type(htmlWidget) = "roHtmlWidget" then ua$ = htmlWidget.GetUserAgent() ' Prepend custom user agent string on to standard Html Widget string p% = instr(1, ua$, ")") if p% > 0 then ua$ = bsp.userAgent$ + mid(ua$, p%+1) htmlWidget.SetUserAgent(ua$) endif endif End Sub 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) SetUserAgentForHtmlWidget(m.bsp, m.stateMachine.loadingHtmlWidget) if m.bsp.sign.htmlEnableJavascriptConsole then m.stateMachine.loadingHtmlWidget.StartInspectorServer(2999) endif if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then m.stateMachine.loadingHtmlWidget.SetPortrait(true) else if m.bsp.htmlSetTransformSupported and m.bsp.sign.monitorOrientation = "portraitbottomonright" then m.stateMachine.loadingHtmlWidget.SetTransform("rot270") endif endif m.stateMachine.loadingHtmlWidget.SetPort(m.bsp.msgPort) securityParams = {} securityParams.websecurity = not m.enableExternalData securityParams.camera_enabled = m.enableCamera m.stateMachine.loadingHtmlWidget.EnableSecurity(securityParams) m.stateMachine.loadingHtmlWidget.EnableMouseEvents(m.enableMouseEvents) if m.hwzOn then m.stateMachine.loadingHtmlWidget.SetHWZDefault("on") endif m.stateMachine.loadingHtmlWidget.EnableScrollBars(m.enableScrollBars) m.stateMachine.loadingHtmlWidget.EnableJavascript(true) if m.enableExternalData then m.stateMachine.loadingHtmlWidget.AllowJavaScriptUrls({ all: "*" }) endif ' only used for obsolete format xml if m.bsp.sign.htmlEnableLocalStorage then m.stateMachine.loadingHtmlWidget.SetLocalStorageDir("localstorage") m.stateMachine.loadingHtmlWidget.SetLocalStorageQuota(m.bsp.sign.htmlLocalStorageSize% * 1024 * 1024) 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 m.url$ = m.url$ + m.queryString.GetCurrentParameterValue() for each customFont in m.customFonts customFontPath$ = GetPoolFilePath(m.bsp.assetPoolFiles, customFont) m.stateMachine.loadingHtmlWidget.AddFont(customFontPath$) next if m.useUserStylesheet then fileName$ = GetPoolFilePath(m.bsp.assetPoolFiles, m.userStylesheet) m.stateMachine.loadingHtmlWidget.SetUserStylesheet(fileName$) endif if not m.stateMachine.isVisible then m.stateMachine.loadingHtmlWidget.Hide() endif 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() ' Do a swap instead of just an assignment m.stateMachine.onDisplayHtmlWidget = m.stateMachine.displayedHtmlWidget m.stateMachine.displayedHtmlWidget = m.stateMachine.loadingHtmlWidget m.stateMachine.ShowHtmlWidget() m.stateMachine.onDisplayHtmlWidget = invalid 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 if m.navigateToLastSelectedOnEntry then ' check to see if prior state was interactive menu (and is not referring to the current state) if type(m.stateMachine.previousStateName$) = "roString" then if m.stateMachine.previousStateName$ <> m.id$ then previousState = m.stateMachine.stateTable[m.stateMachine.previousStateName$] if type(previousState) = "roAssociativeArray" then if previousState.type$ = "interactiveMenuItem" then m.currentInteractiveMenuNavigationIndex% = m.lastInteractiveMenuNavigationIndex% endif endif endif endif endif m.currentInteractiveMenuItem = m.interactiveMenuItems[m.currentInteractiveMenuNavigationIndex%] endif m.DrawInteractiveMenu() m.bsp.SetTouchRegions(m) ' byte arrays to store stream byte input m.serialStreamInputBuffers = CreateObject("roArray", 8, true) for i% = 0 to 7 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, m.currentInteractiveMenuItem) 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.lastInteractiveMenuNavigationIndex% = m.currentInteractiveMenuNavigationIndex% 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, m.currentInteractiveMenuItem) 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 if ItemIsEncrypted(interactiveMenuItem) then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif if m.stateMachine.mosaicDecoderName <> "" then aa.Decoder = m.stateMachine.mosaicDecoderName 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, interactiveMenuItem As Object) ' 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 aa = {} aa.filename = filePath$ if ItemIsEncrypted(interactiveMenuItem) then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif ok = m.stateMachine.imagePlayer.DisplayFile(aa) 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$) isEncrypted = m.bsp.encryptionByFile.DoesExist(file$) 'isEncrypted = m.bsp.contentEncrypted aa = {} aa.filename = filePath$ if isEncrypted then aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", file$) endif ok = m.stateMachine.imagePlayer.DisplayFile(aa) if not ok then m.bsp.diagnostics.PrintDebug("Error preloading file in DrawInteractiveMenu: " + file$ + ", " + filePath$) else m.bsp.diagnostics.PrintDebug("Displayed file in DisplayImage: " + file$) 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.lastInteractiveMenuNavigationIndex% = m.currentInteractiveMenuNavigationIndex% 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$) if type(m.bsp.encryptionByFile) = "roAssociativeArray" and m.bsp.encryptionByFile.DoesExist(fileName$) and m.bsp.contentEncryptionSupported then 'if m.bsp.contentEncrypted then aa = {} aa.filename = filePath$ aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", fileName$) ok = m.stateMachine.imagePlayer.OverlayImage(aa, interactiveMenuItem.x%, interactiveMenuItem.y%) else ok = m.stateMachine.imagePlayer.OverlayImage(filePath$, interactiveMenuItem.x%, interactiveMenuItem.y%) endif 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 ClearTemplateItems() m.stateMachine.canvasWidget.EnableAutoRedraw(0) numLayers% = m.stateMachine.templateObjectsByLayer.Count() for i% = 0 to numLayers% - 1 m.stateMachine.canvasWidget.ClearLayer(i% + 1) next m.stateMachine.canvasWidget.EnableAutoRedraw(1) 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 templateObjects = m.stateMachine.templateObjectsByLayer[i%] ' must support multiple objects per layer??!! templateObject = templateObjects[0] if type(templateObject) = "roAssociativeArray" and templateObject.DoesExist("name") then name$ = templateObject["name"] if type(m.bsp.encryptionByFile) = "roAssociativeArray" and m.bsp.encryptionByFile.DoesExist(name$) then templateObject.EncryptionAlgorithm = "AesCtrHmac" templateObject.EncryptionKey = name$ endif endif if m.bsp.contentEncrypted and type(templateObject) = "roAssociativeArray" and templateObject.DoesExist("fileNameForEncryption") then name$ = templateObject["fileNameForEncryption"] if name$ <> "" and type(m.currentFeed) = "roAssociativeArray" and type(m.currentFeed.liveDataFeed) = "roAssociativeArray" then if (m.currentFeed.liveDataFeed.isDynamicPlaylist or m.currentFeed.liveDataFeed.isLiveMediaFeed) then templateObject.EncryptionAlgorithm = "AesCtrHmac" templateObject.EncryptionKey = name$ endif endif endif m.stateMachine.canvasWidget.SetLayer(templateObject, i% + 1) else m.stateMachine.canvasWidget.ClearLayer(i% + 1) endif next for i% = numLayers% to numLayers% + 2 m.stateMachine.canvasWidget.ClearLayer(i% + 1) 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 else if templateItem.systemVariableType$ = "BrightAuthorVersion" then text["text"] = m.bsp.sysInfo.baVersion$ 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%] else textValue$ = "" endif text = { } text["text"] = textValue$ m.BuildTextTemplateItem(templateItem, text) endif else title$ = templateItem.title.GetCurrentParameterValue() if type(liveDataFeed.articlesByTitle) = "roAssociativeArray" then if liveDataFeed.articlesByTitle.DoesExist(title$) then textValue$ = liveDataFeed.articlesByTitle.Lookup(title$) else textValue$ = "" endif text = { } text["text"] = textValue$ m.BuildTextTemplateItem(templateItem, text) endif endif endif else if templateItem.type$ = "imageTemplateItem" then image = { } image["name"] = templateItem.fileName$ 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" or templateItem.type$ = "mrssMediaTemplateItem" then if type(templateItem.fileName$) <> "Invalid" and templateItem.fileName$ <> "" then image = { } if type(templateItem.fileNameForEncryption) = "roString" then image["fileNameForEncryption"] = templateItem.fileNameForEncryption else image["fileNameForEncryption"] = "" endif 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" or type(m.mrssMediaTemplateItem) = "roAssociativeArray" then m.mrssMediaTemplateItem.fileName$ = invalid endif if type(m.mrssCustomFieldTemplateItems) = "roAssociativeArray" then for each customFieldName in m.mrssCustomFieldTemplateItems customFieldTemplateItem = m.mrssCustomFieldTemplateItems[customFieldName] customFieldTemplateItem.textString$ = invalid next endif End Sub Function STTemplatePlayingEventHandler(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.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["name"] = file$ 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 if backgroundImage.DoesExist("name") then name$ = backgroundImage["name"] if type(m.bsp.encryptionByFile) = "roAssociativeArray" and m.bsp.encryptionByFile.DoesExist(name$) then 'if m.bsp.contentEncrypted then backgroundImage.EncryptionAlgorithm = "AesCtrHmac" backgroundImage.EncryptionKey = name$ endif 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 templateObjects = m.stateMachine.templateObjectsByLayer[i%] ' must support multiple objects per layer??!! templateObject = templateObjects[0] if type(templateObject) = "roAssociativeArray" and templateObject.DoesExist("name") then name$ = templateObject["name"] if type(m.bsp.encryptionByFile) = "roAssociativeArray" and m.bsp.encryptionByFile.DoesExist(name$) then templateObject.EncryptionAlgorithm = "AesCtrHmac" templateObject.EncryptionKey = name$ endif endif m.stateMachine.canvasWidget.SetLayer(templateObject, 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") ' resize the video player in case it was used by an MRSS feed within this state if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() m.stateMachine.videoPlayer.SetRectangle(m.stateMachine.rectangle) endif 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) = "roVideoEvent" and type(m.stateMachine.videoPlayer) = "roVideoPlayer" and event.GetSourceIdentity() = m.stateMachine.videoPlayer.GetIdentity() and event.GetInt() = MEDIA_END then m.GetMRSSTemplateItem() return "HANDLED" 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.AllContentExists( 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 if m.currentFeed.items.Count() = 0 then return invalid endif 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] if displayItem = invalid then return invalid endif 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 ' no valid content - clear old content if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.StopDisplay() endif m.ClearTemplateItems() m.LaunchWaitForContentTimer() return endif ' check to see if the item is an image or a video. ' if an image, set a timer ' if a video, need to wait for media end event if isImage(item) then m.mrssItemTimer.SetElapsed(item.duration, 0) m.mrssItemTimer.Start() endif if type(m.mrssTitleTemplateItem) = "roAssociativeArray" then m.mrssTitleTemplateItem.textString$ = item.title endif if type(m.mrssDescriptionTemplateItem) = "roAssociativeArray" then m.mrssDescriptionTemplateItem.textString$ = item.description endif ' need to distinguish here between image items and video items m.mrssVideoTemplateItem = {} if type(m.mrssMediaTemplateItem) = "roAssociativeArray" then if isImage(item) then m.mrssMediaTemplateItem.fileName$ = item.filePath$ m.mrssMediaTemplateItem.fileNameForEncryption = item.title m.mrssVideoTemplateItem.fileName$ = "" else m.mrssMediaTemplateItem.fileName$ = "" m.mrssVideoTemplateItem.fileName$ = item.filePath$ m.mrssVideoTemplateItem.fileNameForEncryption = item.title endif endif if type(m.mrssCustomFieldTemplateItems) = "roAssociativeArray" then for each customFieldName in m.mrssCustomFieldTemplateItems customFieldTemplateItem = m.mrssCustomFieldTemplateItems[customFieldName] ' see if the corresponding custom field exists in this item customFieldValue = item.mrssCustomFields.lookup(customFieldName) if type(customFieldValue) = "roString" then customFieldTemplateItem.textString$ = customFieldValue endif next endif ' different/additional path needed to display video if type(m.mrssVideoTemplateItem) = "roAssociativeArray" and m.mrssVideoTemplateItem.fileName$ <> invalid and m.mrssVideoTemplateItem.fileName$ <> "" and type(m.stateMachine.videoPlayer) = "roVideoPlayer" then ' ensure that the image gets cleared item = m.mrssMediaTemplateItem r = CreateObject("roRectangle", item.x% + m.stateMachine.rectangle.getX(), item.y% + m.stateMachine.rectangle.getY(), item.width%, item.height%) m.stateMachine.videoPlayer.SetRectangle(r) m.videoItem = {} m.videoItem.videoDisplayMode% = 0 m.videoItem.automaticallyLoop = false m.videoItem.fileName$ = m.mrssVideoTemplateItem.fileNameForEncryption m.videoItem.filePath$ = m.mrssVideoTemplateItem.fileName$ m.stateMachine.preloadState = invalid m.stateMachine.syncInfo = invalid m.videoItem.userVariable = invalid m.videoTimeCodeEvents = invalid m.videoItem.isEncrypted = false if m.bsp.contentEncrypted and type(m.currentFeed) = "roAssociativeArray" and type(m.currentFeed.liveDataFeed) = "roAssociativeArray" then if m.currentFeed.liveDataFeed.isDynamicPlaylist or m.currentFeed.liveDataFeed.isLiveMediaFeed then m.videoItem.isEncrypted = true endif endif m.PlayVideo(false, true) else if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif 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() if not m.stateMachine.useVideoPlayerForImages then ' set default transition if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.SetDefaultTransition(m.slideTransition%) endif 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 ' distinguish between a feed that has no content and a feed in which no content has been downloaded if m.currentFeed.items.Count() = 0 or not m.currentFeed.AllContentExists( m.assetPoolFiles ) then ' no content in feed - send a message to self to trigger exit from state (like video playback failure) mrssNotFullyLoadedPlaybackEvent = CreateObject("roAssociativeArray") mrssNotFullyLoadedPlaybackEvent["EventType"] = "MRSSNotFullyLoadedPlaybackEvent" mrssNotFullyLoadedPlaybackEvent["EventParameter"] = m.liveDataFeed.name$ m.stateMachine.msgPort.PostMessage(mrssNotFullyLoadedPlaybackEvent) else m.AdvanceToNextMRSSItem() endif else ' this situation will occur when the feed itself has not downloaded yet - send a message to self to trigger exit from state (like video playback failure) mrssNotFullyLoadedPlaybackEvent = CreateObject("roAssociativeArray") mrssNotFullyLoadedPlaybackEvent["EventType"] = "MRSSNotFullyLoadedPlaybackEvent" mrssNotFullyLoadedPlaybackEvent["EventParameter"] = m.liveDataFeed.name$ m.stateMachine.msgPort.PostMessage(mrssNotFullyLoadedPlaybackEvent) return "HANDLED" 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 type(m.currentFeed) <> "roAssociativeArray" then return "HANDLED" endif 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"] = "MRSSNotFullyLoadedPlaybackEvent" then liveDataFeedName$ = event["EventParameter"] if liveDataFeedName$ = m.liveDataFeed.name$ then if type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else if type(m.currentFeed) = "roAssociativeArray" and m.currentFeed.ContentExists( m.assetPoolFiles ) then m.AdvanceToNextMRSSItem() '' redundant check '' else if type(m.currentFeed) = "roAssociativeArray" and type(m.currentFeed.items) = "roArray" and m.currentFeed.items.Count() = 0 then '' m.LaunchWaitForContentTimer() else m.LaunchWaitForContentTimer() endif endif return "HANDLED" 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 ) ' feed may have been downloaded but it might not have content yet (empty mrss feed) ' or feed has been downloaded but not all of its content has been downloaded yet - in this case, move on to the next item if possible if m.currentFeed.items.Count() = 0 or not m.currentFeed.AllContentExists( m.assetPoolFiles ) if type(m.currentFeed) = "roAssociativeArray" and m.currentFeed.ContentExists( m.assetPoolFiles ) then m.AdvanceToNextMRSSItem() else if type(m.signChannelEndEvent) = "roAssociativeArray" then return m.ExecuteTransition(m.signChannelEndEvent, stateData, "") else m.LaunchWaitForContentTimer() return "HANDLED" endif endif ' all content exists - display an item m.displayIndex = 0 m.AdvanceToNextMRSSItem() 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) = "roHtmlWidgetEvent" and type(m.stateMachine.loadingHtmlWidget) = "roHtmlWidget" then eventData = event.GetData() if type(eventData) = "roAssociativeArray" and type(eventData.reason) = "roString" then userData = event.GetUserData() if userData <> invalid and userData.stateId$ <> invalid and userData.stateId$ = m.id$ 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) m.AdvanceToNextMRSSItem() 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() ' Do a swap instead of just an assignment m.stateMachine.onDisplayHtmlWidget = m.stateMachine.displayedHtmlWidget m.stateMachine.displayedHtmlWidget = m.stateMachine.loadingHtmlWidget m.stateMachine.ShowHtmlWidget() m.stateMachine.onDisplayHtmlWidget = invalid endif 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 type(m.currentFeed) <> "roAssociativeArray" or not m.currentFeed.AllContentExists( m.assetPoolFiles ) then if type(m.currentFeed) = "roAssociativeArray" and m.currentFeed.ContentExists( m.assetPoolFiles ) then if m.displayIndex = invalid then m.displayIndex = 0 endif m.AdvanceToNextMRSSItem() else m.LaunchWaitForContentTimer() endif else if type(m.currentFeed) = "roAssociativeArray" and type(m.currentFeed.items) = "roArray" and m.currentFeed.items.Count() = 0 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 m.currentFeed.items.Count() = 0 or not m.currentFeed.AllContentExists( m.assetPoolFiles ) then if type(m.currentFeed) = "roAssociativeArray" and m.currentFeed.ContentExists( m.assetPoolFiles ) then if m.displayIndex = invalid then m.displayIndex = 0 endif m.AdvanceToNextMRSSItem() else m.LaunchWaitForContentTimer() endif return endif endif endif displayItem = m.currentFeed.items[m.displayIndex] if isHtml(displayItem) then if displayItem.type = "application/widget" then filePath$ = GetPoolFilePath(m.assetPoolFiles, displayItem.url) if filePath$ <> "" then widgetPath$ = m.GetHtmlWidgetFilePath(displayItem, filePath$) if widgetPath$ <> invalid then m.DisplayMRSSItem( displayItem, widgetPath$ ) displayedItem = true end if end if else if displayItem.type = "text/html" then m.DisplayMRSSItem( displayItem, displayItem.url ) displayedItem = true endif else 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 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 if IsNonEmptyString(displayItem.guid) then asset.change_hint = displayItem.guid else if IsString(displayItem.url) then asset.change_hint = displayItem.url endif 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 ItemIsEncrypted(displayItem) and (m.liveDataFeed.isDynamicPlaylist or m.liveDataFeed.isLiveMediaFeed) isEncrypted = true else isEncrypted = false endif if isImage(displayItem) then m.imageItem = {} if IsString(displayItem.title) then m.imageItem.fileName$ = displayItem.title else m.imageItem.fileName$ = "noTitle" endif m.imageItem.filePath$ = filePath$ m.imageItem.userVariable = invalid m.imageItem.slideDelayInterval% = displayItem.duration m.imageItem.useImageBuffer = false m.imageItem.isEncrypted = isEncrypted ' m.DisplayImage("mrss") m.DrawImage(false) if not m.stateMachine.useVideoPlayerForImages then if type(m.stateMachine.videoPlayer) = "roVideoPlayer" then m.stateMachine.videoPlayer.StopClear() endif 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 if type(m.stateMachine.imagePlayer) = "roImageWidget" then m.stateMachine.imagePlayer.SetDefaultTransition(m.slideTransition%) endif else if isAudio(displayItem) fileName$ = displayItem.title 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, fileName$) endif ' playback logging m.stateMachine.LogPlayStart("audio", fileName$) endif endif else if isHtml(displayItem) m.stateMachine.loadingHtmlWidget = CreateObject("roHtmlWidget", m.stateMachine.rectangle) SetUserAgentForHtmlWidget(m.bsp, m.stateMachine.loadingHtmlWidget) userData = {} userData.stateId$ = m.id$ m.stateMachine.loadingHtmlWidget.SetUserData(userData) if m.bsp.sign.htmlEnableJavascriptConsole then m.stateMachine.loadingHtmlWidget.StartInspectorServer(2999) endif if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then m.stateMachine.loadingHtmlWidget.SetPortrait(true) else if m.bsp.htmlSetTransformSupported and m.bsp.sign.monitorOrientation = "portraitbottomonright" then m.stateMachine.loadingHtmlWidget.SetTransform("rot270") endif endif m.stateMachine.loadingHtmlWidget.SetPort(m.bsp.msgPort) if m.bsp.sign.htmlEnableLocalStorage then m.stateMachine.loadingHtmlWidget.SetLocalStorageDir("localstorage") m.stateMachine.loadingHtmlWidget.SetLocalStorageQuota(m.bsp.sign.htmlLocalStorageSize% * 1024 * 1024) endif m.stateMachine.loadingHtmlWidget.SetUrl(filePath$) 'print "Loading MRSS HTML path: ";filePath$ ' ?? call LaunchTimer here to handle load timeout ?? 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(displayItem.duration, 0) m.imageTimeoutTimer.Start() else m.videoItem = {} m.videoItem.fileName$ = displayItem.title m.videoItem.filePath$ = filePath$ m.videoItem.userVariable = invalid m.videoItem.probeData = invalid m.videoItem.automaticallyLoop = true m.videoItem.videoDisplayMode% = 0 m.videoItem.isEncrypted = isEncrypted ' 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 Function LastPathComponent(path$ As String) As String p% = instr(1, path$, "/") t% = p% while t% > 0 t% = instr(p%+1, path$, "/") if t% > 0 then p% = t% endif end while if p% > 0 then return mid(path$, p%+1) endif return path$ End Function Function GetHtmlWidgetFilePath( displayItem As Object, filePath$ As String) As String ' Get file name from url as widget name widgetName$ = LastPathComponent(displayItem.url) widgetDir$ = "/htmlWidgets/" + widgetName$ + "/" CreateDirectory(widgetDir$) ' TODO - check to see if files have already been unzipped and skip unzip if so ?? unzipper = CreateObject("roBrightPackage", filePath$) unzipper.Unpack(widgetDir$) return "file:" + widgetDir$ + "index.html" End Function 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$ if type(m.audioItem.filePath$) = "roString" then filePath$ = m.audioItem.filePath$ else filePath$ = GetPoolFilePath(m.bsp.assetPoolFiles, m.audioItem.fileName$) endif 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 AUDIO_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 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) else if event.GetInt() = AUDIO_TIME_CODE then audioTimeCodeIndex$ = str(event.GetData()) m.bsp.diagnostics.PrintDebug("Audio TimeCode Event " + audioTimeCodeIndex$) if type(m.audioTimeCodeEvents) = "roAssociativeArray" then audioTimeCodeEvent = m.audioTimeCodeEvents[audioTimeCodeIndex$] if type(audioTimeCodeEvent) = "roAssociativeArray" then m.bsp.ExecuteTransitionCommands(m.stateMachine, audioTimeCodeEvent) m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "audioTimeCode", "", "1") return "HANDLED" endif m.bsp.logging.WriteEventLogEntry(m.stateMachine, m.id$, "audioTimeCode", "", "0") endif 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 if CanUseScreenModes({}) then ' no need to rotate per zone if already rotated by screen else if m.bsp.DeviceSupportsRotation() then if m.bsp.sign.monitorOrientation = "portrait" then videoPlayer.SetTransform("rot90") else if m.bsp.sign.monitorOrientation = "portraitbottomonright" then videoPlayer.SetTransform("rot270") endif endif videoPlayer.SetRectangle(zoneHSM.rectangle) videoPlayer.SetPort(zoneHSM.msgPort) ' refactor to 'not puma, not panther' if m.bsp.sysInfo.deviceFamily$ = "cheetah" or m.bsp.sysInfo.deviceFamily$ = "tiger" or m.bsp.sysInfo.deviceFamily$ = "lynx" or m.bsp.sysInfo.deviceFamily$ = "impala" or m.bsp.sysInfo.deviceFamily$ = "malibu" or m.bsp.sysInfo.deviceFamily$ = "pantera" 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$) aa = {} m.bsp.SetEncryptionAttributes(aa, file$, filePath$) if m.bsp.contentEncryptionSupported then ok = m.stateMachine.videoPlayer.PlayStaticImage(aa) else ok = m.stateMachine.videoPlayer.PlayStaticImage(filePath$) endif 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$) aa = {} m.bsp.SetEncryptionAttributes(aa, zoneHSM.backgroundBitmapFile$, backgroundBitmapFilePath$) if m.bsp.contentEncryptionSupported then zoneHSM.widget.SetBackgroundBitmap(aa, zoneHSM.stretch%) else zoneHSM.widget.SetBackgroundBitmap(backgroundBitmapFilePath$, zoneHSM.stretch%) endif 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" and m.stateMachine.isVisible 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())) zoneHSM.scrollSpeed% = 100 if type(zoneXML.zoneSpecificParameters.scrollSpeed) = "roXMLList" then scrollSpeed$ = zoneXML.zoneSpecificParameters.scrollSpeed[0].GetText() if scrollSpeed$ <> "" then zoneHSM.scrollSpeed% = int(val(scrollSpeed$)) 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.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 zoneHSM.scrollingMethod% = 3 then zoneHSM.widget.SetAnimationSpeed(zoneHSM.scrollSpeed%) endif 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$) aa = {} m.bsp.SetEncryptionAttributes(aa, zoneHSM.backgroundBitmapFile$, backgroundBitmapFilePath$) if m.bsp.contentEncryptionSupported then zoneHSM.widget.SetBackgroundBitmap(aa, zoneHSM.stretch%) else zoneHSM.widget.SetBackgroundBitmap(backgroundBitmapFilePath$, zoneHSM.stretch%) endif 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 Sub SetEncryptionAttributes(aa As Object, fileName$ As String, filePath$ As String) aa.fileName = filePath$ if type(m.encryptionByFile) = "roAssociativeArray" then if m.encryptionByFile.DoesExist(fileName$) then ' if m.bsp.contentEncrypted aa.AddReplace("EncryptionAlgorithm", "AesCtrHmac") aa.AddReplace("EncryptionKey", fileName$) endif endif 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 if m.stateMachine.isVisible then 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"] = "USER_VARIABLES_UPDATED" then rssDataFeedsUpdated = false userVariables = m.bsp.currentUserVariables for each rssDataFeedItem in m.stateMachine.rssDataFeedItems if rssDataFeedItem.isUserVariable then for each userVariableKey in userVariables userVariable = userVariables.Lookup(userVariableKey) if userVariable.name$ = rssDataFeedItem.userVariableName then tickerItemValue = userVariable.GetCurrentValue() rssDataFeedItem.textStrings = [] rssDataFeedItem.textStrings.push(tickerItemValue) rssDataFeedsUpdated = true endif next endif next if rssDataFeedsUpdated then m.PopulateRSSDataFeedWidget() endif return "HANDLED" 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.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("") ' 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 Sub InitiateRemoteSnapshotTimer() m.remoteSnapshotTimer = CreateObject("roTimer") m.remoteSnapshotTimer.SetPort(m.msgPort) m.remoteSnapshotTimer.SetElapsed(m.globalAA.remoteSnapshotInterval, 0) m.remoteSnapshotTimer.Start() End Sub Sub RemoveRemoteSnapshotTimer() if type(m.remoteSnapshotTimer) = "roTimer" then m.remoteSnapshotTimer.Stop() m.remoteSnapshotTimer = invalid endif End Sub 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") if m.bsp.globalAA.enableRemoteSnapshot then m.bsp.InitiateRemoteSnapshotTimer() 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" then m.bsp.diagnostics.PrintDebug("STPlayerEventHandler - PREPARE_FOR_RESTART") m.bsp.touchScreen = invalid m.bsp.btManager.ResetPresentationBeacons() 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) = "roControlDisconnected" then userData$ = "Port unspecified" if type(event.getUserData()) = "roString" then userData$ = event.getUserData() endif m.bsp.diagnostics.PrintDebug("### Control port disconnected: " + userData$ ) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_CONTROL_PORT_DISCONNECTED, userData$) m.bsp.logging.FlushLogFile() RebootSystem() return "HANDLED" 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) = "roTimerEvent" then if type(m.bsp.remoteSnapshotTimer) = "roTimer" and stri(event.GetSourceIdentity()) = stri(m.bsp.remoteSnapshotTimer.GetIdentity()) then presentationName$ = "" if m.bsp.activePresentation$ <> invalid then presentationName$ = m.bsp.activePresentation$ endif ok = TakeSnapshot(m.bsp.systemTime, presentationName$) m.bsp.remoteSnapshotTimer.SetElapsed(m.bsp.globalAA.remoteSnapshotInterval, 0) m.bsp.remoteSnapshotTimer.Start() endif 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.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 if type(m.bsp.serialPortsToRetry) = "roAssociativeArray" then for each serialPortToRetryName in m.bsp.serialPortsToRetry serialPortToRetry = m.bsp.serialPortsToRetry[serialPortToRetryName] timer = serialPortToRetry.timer if stri(event.GetSourceIdentity()) = stri(timer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("RetryCreateSerial timeout") ok = m.bsp.RetryCreateSerial(serialPortToRetry.port$, serialPortToRetry.outputOnly) if not ok then m.bsp.diagnostics.PrintDebug("RetryCreateSerial failure, restart timer") timer.SetElapsed(15, 0) timer.Start() endif endif next 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) = "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) = "roAssetFetcherProgressEvent" then print "### File download progress ";event.GetFileName();" unknown" userData$ = event.GetUserData() for each liveDataFeedName in m.bsp.liveDataFeeds if userData$ = liveDataFeedName then liveDataFeed = m.bsp.liveDataFeeds.Lookup(userData$) liveDataFeed.HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent(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" + hour$ 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 else if type(event) = "roBtEvent" then m.bsp.btManager.HandleEvent(event) return "HANDLED" endif stateData.nextState = m.superState return "SUPER" End Function Sub DeleteExcessSnapshots(globalAA As Object) while globalAA.listOfSnapshotFiles.Count() >= globalAA.remoteSnapshotMaxImages and globalAA.listOfSnapshotFiles.Count() > 0 fileToDelete = globalAA.listOfSnapshotFiles.Shift() if type(globalAA.bsp.networkingHSM) = "roAssociativeArray" then if type(globalAA.bsp.networkingHSM.uploadSnapshotUrl) = "roUrlTransfer" then if lcase(globalAA.bsp.networkingHSM.uploadSnapshotUrl.getUserData().lookup("name")) = lcase(fileToDelete) then globalAA.bsp.diagnostics.PrintDebug("DeleteExcessSnapshots: roUrlTransfer in process for "+fileToDelete) globalAA.bsp.NetworkingHSM.uploadSnapshotUrl.AsyncCancel() globalAA.bsp.NetworkingHSM.uploadSnapshotUrl = invalid ' and setup the retry timer if type(globalAA.bsp.networkingHSM.retrySnapshotUploadTimer) <> "roTimer" then globalAA.bsp.networkingHSM.retrySnapshotUploadTimer = CreateObject("roTimer") globalAA.bsp.networkingHSM.retrySnapshotUploadTimer.SetPort(globalAA.bsp.networkingHSM.msgPort) globalAA.bsp.networkingHSM.retrySnapshotUploadTimer.SetElapsed(30, 0) '30 seconds seems to be what this gets set to other places. globalAA.bsp.networkingHSM.retrySnapshotUploadTimer.Start() endif endif endif if globalAA.bsp.networkingHSM.pendingSnapshotsToUpload.DoesExist(fileToDelete) then globalAA.bsp.diagnostics.PrintDebug("DeleteExcessSnapshots: removed from pendingSnapshotsToUpload: "+fileToDelete) globalAA.bsp.networkingHSM.pendingSnapshotsToUpload.Delete(fileToDelete) endif endif ok = DeleteFile("snapshots/" + fileToDelete) end while End Sub Sub TakeSnapshot(systemTime As Object, activePresentation$ As String) globalAA = GetGlobalAA() ' before taking snapshot, delete the oldest if necessary DeleteExcessSnapshots(globalAA) videoMode = CreateObject("roVideoMode") ' create a file name based on the current date/time dtLocal = systemTime.GetLocalDateTime() ' strip illegal characters from string dateTime$ = GetISODateTimeString(dtLocal) aa = {} aa.filename = "snapshots/" + dateTime$ + ".jpg" aa.Width = videoMode.GetResX() aa.Height = videoMode.GetResY() aa.filetype = "JPEG" aa.quality = globalAA.remoteSnapshotJpegQualityLevel aa.Async = 0 if type(activePresentation$) = "roString" then aa.description = activePresentation$ endif if type(globalAA.bsp) = "roAssociativeArray" then globalAA.bsp.diagnostics.PrintDebug( "------------------------------------------- SCREENSHOT " + aa.filename + "-------------------------------------") endif ok = videoMode.Screenshot(aa) if not ok then if type(globalAA.bsp) = "roAssociativeArray" then globalAA.bsp.diagnostics.PrintDebug("TakeSnapshot: not ok returned from Screenshot") globalAA.bsp.logging.WriteDiagnosticLogEntry(globalAA.bsp.diagnosticCodes.EVENT_SCREENSHOT_ERROR, "") endif else globalAA.listOfSnapshotFiles.push(dateTime$ + ".jpg") ' upload snapshot to BSN if it's initialized if type(globalAA.bsp) = "roAssociativeArray" then snapshotCaptured = CreateObject("roAssociativeArray") snapshotCaptured["EventType"] = "SNAPSHOT_CAPTURED" snapshotCaptured["SnapshotName"] = dateTime$ + ".jpg" globalAA.bsp.msgPort.PostMessage(snapshotCaptured) endif endif videoMode = invalid End Sub Function GetISODateTimeString(date As Object) As String isoDateTime$ = date.ToIsoString() index = instr(1, isoDateTime$, ",") if index >= 1 then isoDateTime$ = mid(isoDateTime$, 1, index - 1) endif return isoDateTime$ End Function Sub QueueSnapshotForBSN(snapshotName$ As String, url As String) ' systemTime = m.stateMachine.systemTime utcDateTime = m.stateMachine.systemTime.GetUtcDateTime() localDateTime = m.stateMachine.systemTime.GetLocalDateTime() headers = {} headers["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8" headers["host"] = m.stateMachine.AwsSqsHost headers["User-Agent"] = m.bsp.userAgent$ headers["X-Amz-Content-SHA256"] = "" xAmzDate$ = Left(utcDateTime.ToIsoString(), 15) + "Z" headers["X-Amz-Date"] = xAmzDate$ headers["x-amz-security-token"] = m.stateMachine.awsSessionToken$ s0 = chr(34) + ":" + chr(34) s1 = chr(34) + ", " + chr(34) localTimestamp$ = FormatDateTime(localDateTime) + "0000" utcTimestamp$ = FormatDateTime(utcDateTime) + "0000Z" account$ = m.stateMachine.currentSync.LookupMetadata("server", "account") user$ = m.stateMachine.currentSync.LookupMetadata("server", "user") password$ = m.stateMachine.currentSync.LookupMetadata("server", "password") group$ = m.stateMachine.currentSync.LookupMetadata("server", "group") serialNumber$ = m.bsp.sysInfo.deviceUniqueID$ if type(m.bsp.activePresentation$) = "roString" then presentationName$ = m.bsp.activePresentation$ else presentationName$ = "" endif jsonString = "{" + chr(34) + "AccountName" + s0 + account$ + s1 + "DeviceSerial" + s0 + serialNumber$ + s1 + "Login" + s0 + user$ + s1 + "Password" + s0 + password$ + s1 + "GroupName" + s0 + group$ + s1 + "PresentationName" + s0 + presentationName$ + s1 + "ScreenshotUrl" + s0 + url + s1 + "LocalTimestamp" + s0 + localTimestamp$ + s1 + "UTCTimestamp" + s0 + utcTimestamp$ + chr(34) + " }" parameters = {} parameters["Action"] = "SendMessage" parameters["MessageBody"] = jsonString parameters["Version"] = "2012-11-05" payload = GetRequestPayload(parameters) headers["X-Amz-Content-SHA256"] = ComputeSHA256Hash(payload) canonicalRequest = CanonicalizeRequest(m.stateMachine.AwsSqsAbsolutePath, "POST", headers, headers["X-Amz-Content-SHA256"]) signature = ComputeSignature(m.stateMachine.awsAccessKeyId$, m.stateMachine.awsSecretAccessKey$, m.stateMachine.AwsSqsRegion, utcDateTime, m.stateMachine.AwsSqsService, CanonicalizeHeaderNames(headers), canonicalRequest) authorizationHeader$ = "AWS4-HMAC-SHA256" authorizationHeader$ = authorizationHeader$ + " Credential" + "=" + m.stateMachine.awsAccessKeyId$ + "/" + FormatDateTimeyyyyMMdd(utcDateTime) + "/" + m.stateMachine.AwsSqsRegion + "/" + m.stateMachine.AwsSqsService + "/" + "aws4_request" + "," authorizationHeader$ = authorizationHeader$ + " SignedHeaders" + "=" + "content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-security-token" + "," authorizationHeader$ = authorizationHeader$ + " Signature" + "=" + signature headers["Authorization"] = authorizationHeader$ m.stateMachine.queueSnapshotUrl = CreateObject("roUrlTransfer") m.stateMachine.queueSnapshotUrl.SetUserData(snapshotName$) m.stateMachine.queueSnapshotUrl.SetPort(m.stateMachine.msgPort) m.stateMachine.queueSnapshotUrl.SetUserAgent(m.bsp.userAgent$) requestHeaders = {} if not m.stateMachine.queueSnapshotUrl.AddHeader("Authorization", headers["Authorization"]) then stop if not m.stateMachine.queueSnapshotUrl.AddHeader("Content-Type", headers["Content-Type"]) then stop if not m.stateMachine.queueSnapshotUrl.AddHeader("x-amz-security-token", headers["x-amz-security-token"]) then stop if not m.stateMachine.queueSnapshotUrl.AddHeader("X-Amz-Date", headers["X-Amz-Date"]) then stop if not m.stateMachine.queueSnapshotUrl.AddHeader("X-Amz-Content-SHA256", headers["X-Amz-Content-SHA256"]) then stop if not m.stateMachine.queueSnapshotUrl.SetUrl(m.stateMachine.incomingDeviceScreenshotsQueue$) then stop aa = {} aa.method = "POST" aa.request_body_string = payload aa.response_body_string = true ok = m.stateMachine.queueSnapshotUrl.AsyncMethod(aa) if not ok then stop End Sub Function FormatDateTimeyyyyMMdd(dateTime As Object) As String dt$ = StripLeadingSpaces(stri(dateTime.GetYear())) + AddLeadingZeros(StripLeadingSpaces(stri(dateTime.GetMonth())), 2) + AddLeadingZeros(StripLeadingSpaces(stri(dateTime.GetDay())), 2) return dt$ End Function Function FormatDateTime(dt As Object) As String dt$ = StripLeadingSpaces(stri(dt.GetYear())) dt$ = dt$ + "-" + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetMonth())), 2) dt$ = dt$ + "-" + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetDay())), 2) dt$ = dt$ + "T" + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetHour())), 2) dt$ = dt$ + ":" + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetMinute())), 2) dt$ = dt$ + ":" + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetSecond())), 2) dt$ = dt$ + "." + AddLeadingZeros(StripLeadingSpaces(stri(dt.GetMillisecond())), 3) return dt$ End Function Function AddLeadingZeros(str$ As String, numDigits% As Integer) As String while len(str$) < numDigits% str$ = "0" + str$ end while return str$ End Function Function GetRequestPayload(parameters As Object) ' Action ' MessageBody ' Version xfer = CreateObject("roUrlTransfer") payload$ = "" payload$ = payload$ + GetPayloadItem(xfer, "Action", parameters) payload$ = payload$ + GetPayloadItem(xfer, "MessageBody", parameters) payload$ = payload$ + GetPayloadItem(xfer, "Version", parameters) payload$ = Left(payload$, len(payload$) - 1) return payload$ End Function Function GetPayloadItem(xfer As Object, key As String, values As Object) As String payload$ = key payload$ = payload$ + "=" payload$ = payload$ + xfer.Escape(values[key]) payload$ = payload$ + "&" return payload$ End Function Function ComputeSHA256Hash(str$ As String) bytes = CreateObject("roByteArray") bytes.FromAsciiString(str$) hashGenerator = CreateObject("roHashGenerator", "SHA256") hash = hashGenerator.hash(bytes) hex$ = hash.ToHexString() hex$ = lcase(hex$) return hex$ End Function Function CanonicalizeRequest(resourcePath As String, httpMethod As String, headers As Object, precomputedBodyHash As String) As String canonicalRequest$ = "" canonicalRequest$ = canonicalRequest$ + httpMethod + chr(10) canonicalRequest$ = canonicalRequest$ + resourcePath + chr(10) canonicalRequest$ = canonicalRequest$ + chr(10) canonicalRequest$ = canonicalRequest$ + CanonicalizeHeaders(headers) + chr(10) canonicalRequest$ = canonicalRequest$ + CanonicalizeHeaderNames(headers) + chr(10) canonicalRequest$ = canonicalRequest$ + precomputedBodyHash return canonicalRequest$ End Function Function CanonicalizeHeaders(headers As Object) As String ' Content-Type ' host ' User-Agent ' X-Amz-Content-SHA256 ' X-Amz-Date ' x-amz-security-token canonicalHeaders$ = "" canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "Content-Type") canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "host") canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "User-Agent") canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "X-Amz-Content-SHA256") canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "X-Amz-Date") canonicalHeaders$ = canonicalHeaders$ + AddCanonicalHeader(headers, "x-amz-security-token") return canonicalHeaders$ End Function Function AddCanonicalHeader(headers As Object, entry As String) As String canonicalHeader$ = lcase(entry) canonicalHeader$ = canonicalHeader$ + ":" canonicalHeader$ = canonicalHeader$ + headers[entry] canonicalHeader$ = canonicalHeader$ + chr(10) return canonicalHeader$ End Function Function CanonicalizeHeaderNames(headers As Object) As String ' Content-Type ' host ' User-Agent ' X-Amz-Content-SHA256 ' X-Amz-Date ' x-amz-security-token ' canonicalHeaderNames$ = "" canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("Content-Type") canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("host") canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("User-Agent") canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("X-Amz-Content-SHA256") canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("X-Amz-Date") canonicalHeaderNames$ = canonicalHeaderNames$ + AddCanonicalHeaderName("x-amz-security-token") canonicalHeaderNames$ = Left(canonicalHeaderNames$, len(canonicalHeaderNames$) - 1) return canonicalHeaderNames$ End Function Function AddCanonicalHeaderName(entry As String) As String canonicalHeaderName$ = lcase(entry) canonicalHeaderName$ = canonicalHeaderName$ + ";" return canonicalHeaderName$ End Function Function ComputeSignature(awsAccessKey As String, awsSecretAccessKey As String, region As String, signedAt As Object, service As String, signedHeaders as String, canonicalRequest As String) As String dateStamp1 = FormatDateTimeyyyyMMdd(signedAt) scope = dateStamp1 + "/" + region + "/" + service + "/aws4_request" dateStamp2 = dateStamp1 + "T" + AddLeadingZeros(StripLeadingSpaces(stri(signedAt.GetHour())), 2) + AddLeadingZeros(StripLeadingSpaces(stri(signedAt.GetMinute())), 2) + AddLeadingZeros(StripLeadingSpaces(stri(signedAt.GetSecond())), 2) + "Z" stringToSign = "AWS4-HMAC-SHA256" + chr(10) + dateStamp2 + chr(10) + scope + chr(10) canonicalRequestHash = ComputeSHA256Hash(canonicalRequest) stringToSign = stringToSign + canonicalRequestHash key = ComposeSigningKey(awsSecretAccessKey, region, dateStamp1, service) stringToSignBytes = CreateObject("roByteArray") stringToSignBytes.FromAsciiString(stringToSign) keyedHash = ComputeKeyedHash(key, stringToSignBytes) keyedHash$ = lcase(keyedHash.ToHexString()) return keyedHash$ End Function Function ComposeSigningKey(awsSecretAccessKey As String, region As String, date As String, service As String) As Object ksecretBytes = CreateObject("roByteArray") ksecretBytes.FromAsciiString("AWS4" + awsSecretAccessKey) dateBytes = CreateObject("roByteArray") dateBytes.FromAsciiString(date) hashDate = ComputeKeyedHash(ksecretBytes, dateBytes) regionBytes = CreateObject("roByteArray") regionBytes.FromAsciiString(region) hashRegion = ComputeKeyedHash(hashDate, regionBytes) serviceBytes = CreateObject("roByteArray") serviceBytes.FromAsciiString(service) hashService = ComputeKeyedHash(hashRegion, serviceBytes) aws4RequestBytes = CreateObject("roByteArray") aws4RequestBytes.FromAsciiString("aws4_request") keyedHash = ComputeKeyedHash(hashService, aws4RequestBytes) return keyedHash End Function Function ComputeKeyedHash(key As Object, data As Object) As Object hashGenerator = CreateObject("roHashGenerator", "SHA256") ok = hashGenerator.SetHmacKey(key) if not ok then stop hash = hashGenerator.hash(data) return hash End Function Sub UploadSnapshotToSFN(snapshotName$ As String) headers = {} headers["Content-Type"] = "image/jpeg" headers["SFN-DeviceSerial"] = m.bsp.sysInfo.deviceUniqueID$ if type(m.bsp.activePresentation$) = "roString" then presentationName$ = m.bsp.activePresentation$ else presentationName$ = "" endif headers["SFN-PresentationName"] = presentationName$ localDateTime = m.stateMachine.systemTime.GetLocalDateTime() headers["SFN-LocalTimestamp"] = FormatDateTime(localDateTime) utcDateTime = m.stateMachine.systemTime.GetUtcDateTime() headers["SFN-UTCTimestamp"] = FormatDateTime(utcDateTime) + "Z" snapshotFilePath$ = "snapshots/" + snapshotName$ fileSize% = GetFileSize( snapshotFilePath$ ) headers["Content-Length"] = stri(fileSize%) m.stateMachine.uploadSnapshotToSFNUrl = CreateObject("roUrlTransfer") m.stateMachine.uploadSnapshotToSFNUrl.SetPort(m.stateMachine.msgPort) m.stateMachine.uploadSnapshotToSFNUrl.SetTimeout(5000) m.stateMachine.uploadSnapshotToSFNUrl.SetUrl(m.stateMachine.uploadSnapshotsURL$) m.stateMachine.uploadSnapshotToSFNUrl.SetHeaders(headers) ok = m.stateMachine.uploadSnapshotToSFNUrl.AsyncPutFromFile(snapshotFilePath$) End Sub Sub UploadSnapshotToBSNEE(snapshotName$ As String) headers = {} headers["Content-Type"] = "image/jpeg" ' request.KeepAlive = true; ' headers["Connection"] = "Keep-Alive" headers["BSN-DeviceSerial"] = m.bsp.sysInfo.deviceUniqueID$ headers["BSN-AccountName"] = m.stateMachine.currentSync.LookupMetadata("server", "account") headers["BSN-Login"] = m.stateMachine.currentSync.LookupMetadata("server", "user") headers["BSN-Password"] = m.stateMachine.currentSync.LookupMetadata("server", "password") headers["BSN-GroupName"] = m.stateMachine.currentSync.LookupMetadata("server", "group") if type(m.bsp.activePresentation$) = "roString" then presentationName$ = m.bsp.activePresentation$ else presentationName$ = "" endif headers["BSN-PresentationName"] = presentationName$ localDateTime = m.stateMachine.systemTime.GetLocalDateTime() headers["BSN-LocalTimestamp"] = FormatDateTime(localDateTime) utcDateTime = m.stateMachine.systemTime.GetUtcDateTime() headers["BSN-UTCTimestamp"] = FormatDateTime(utcDateTime) + "Z" headers["BSN-SecurityToken"] = m.stateMachine.securityToken snapshotFilePath$ = "snapshots/" + snapshotName$ fileSize% = GetFileSize( snapshotFilePath$ ) headers["Content-Length"] = stri(fileSize%) m.stateMachine.uploadSnapshotToBSNEEUrl = CreateObject("roUrlTransfer") m.stateMachine.uploadSnapshotToBSNEEUrl.SetPort(m.stateMachine.msgPort) m.stateMachine.uploadSnapshotToBSNEEUrl.SetTimeout(5000) m.stateMachine.uploadSnapshotToBSNEEUrl.SetUrl(m.stateMachine.uploadDeviceScreenshotHandlerAddress) m.stateMachine.uploadSnapshotToBSNEEUrl.SetHeaders(headers) m.stateMachine.uploadSnapshotToBSNEEUrl.SetUserAgent(m.bsp.userAgent$) if not m.stateMachine.uploadSnapshotToBSNEEUrl.AsyncPostFromFile(snapshotFilePath$) then stop End Sub Sub UploadSnapshotToBSN(snapshotName$ As String) if type(m.stateMachine.uploadSnapshotUrl) = "roUrlTransfer" then m.stateMachine.pendingSnapshotsToUpload.AddReplace(snapshotName$, snapshotName$) return endif globalAA = GetGlobalAA() snapshotFilePath$ = "snapshots/" + snapshotName$ serialNumber$ = m.bsp.sysInfo.deviceUniqueID$ photoTimestamp$ = GetPhotoTimestamp(snapshotName$) AWSResourceKey = m.stateMachine.AwsIncomingDirectory + serialNumber$ + "/" + photoTimestamp$ + ".jpg" headers = {} headers["Content-Type"] = "image/jpeg" headers["Content-Length"] = StripLeadingSpaces(stri(GetFileSize(snapshotFilePath$))) headers["x-amz-security-token"] = m.stateMachine.awsSessionToken$ headers["X-Amz-Date"] = GetDateTime(m.stateMachine.systemTime.GetUtcDateTime()) stringToSign$ = m.BuildStringToSign(headers, AWSResourceKey) auth$ = GetHMACSign(stringToSign$, m.stateMachine.awsSecretAccessKey$) authorization$ = "AWS " + m.stateMachine.awsAccessKeyId$ + ":" + auth$ headers["Authorization"] = authorization$ m.stateMachine.uploadSnapshotUrl = CreateObject("roUrlTransfer") ' url to send snapshots to url = m.stateMachine.deviceScreenShotsTemporaryStorage$ + serialNumber$ + "/" + photoTimestamp$ + ".jpg" if not m.stateMachine.uploadSnapshotUrl.SetUrl(url) then stop snapshot = {} snapshot.name = snapshotName$ snapshot.url = url m.stateMachine.uploadSnapshotUrl.SetUserData(snapshot) if not m.stateMachine.uploadSnapshotUrl.AddHeader("Authorization", headers["Authorization"]) then stop if not m.stateMachine.uploadSnapshotUrl.AddHeader("Content-Type", "image/jpeg") then stop if not m.stateMachine.uploadSnapshotUrl.AddHeader("x-amz-security-token", headers["x-amz-security-token"]) then stop if not m.stateMachine.uploadSnapshotUrl.AddHeader("X-Amz-Date", headers["X-Amz-Date"]) then stop m.stateMachine.uploadSnapshotUrl.SetPort(m.stateMachine.msgPort) m.stateMachine.uploadSnapshotUrl.SetTimeout(5000) m.stateMachine.uploadSnapshotUrl.SetUserAgent(m.bsp.userAgent$) ok = m.stateMachine.uploadSnapshotUrl.AsyncPutFromFile(snapshotFilePath$) if not ok then m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SNAPSHOT_PUT_TO_SERVER_ERROR, m.stateMachine.uploadSnapshotUrl.GetFailureReason()) m.bsp.diagnostics.PrintDebug("### AsyncPutFromFile failed: file " + snapshotFilePath$ + ", reason " + m.stateMachine.uploadSnapshotUrl.GetFailureReason()) endif End Sub Function BuildStringToSign(headers As Object, AWSResourceKey As String) As String result$ = "" result$ = result$ + "PUT" result$ = result$ + chr(10) result$ = result$ + chr(10) result$ = result$ + headers["Content-Type"] result$ = result$ + chr(10) result$ = result$ + chr(10) result$ = result$ + "x-amz-date" + ":" + headers["X-Amz-Date"] + chr(10) result$ = result$ + "x-amz-security-token" + ":" + headers["x-amz-security-token"] + chr(10) result$ = result$ + "/" + m.stateMachine.AwsBucketName + "/" + AwsResourceKey return result$ End Function Function GetHMACSign(data As String, key As String) As String binaryData = CreateObject("roByteArray") binaryData.FromAsciiString(data) keyData = CreateObject("roByteArray") keyData.FromAsciiString(key) hashGenerator = CreateObject("roHashGenerator", "SHA1") ok = hashGenerator.SetHmacKey(keyData) bytes = hashGenerator.hash(binaryData) signature = bytes.ToBase64String() return signature End Function ' Return RFC1123 date/time string from roDateTime Function GetDateTime(currentTime As Object) As String daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] dayOfWeek$ = daysOfWeek[currentTime.GetDayOfWeek()] day$ = StripLeadingSpaces(stri(currentTime.GetDay())) if len(day$) = 1 then day$ = "0" + day$ month$ = months[currentTime.GetMonth() - 1] year$ = StripLeadingSpaces(stri(currentTime.GetYear())) hour$ = StripLeadingSpaces(stri(currentTime.GetHour())) if len(hour$) = 1 then hour$ = "0" + hour$ minute$ = StripLeadingSpaces(stri(currentTime.GetMinute())) if len(minute$) = 1 then minute$ = "0" + minute$ second$ = StripLeadingSpaces(stri(currentTime.GetSecond())) if len(second$) = 1 then second$ = "0" + second$ currentDateTime = dayOfWeek$ + ", " + day$ + " " + month$ + " " + year$ + " " + hour$ + ":" + minute$ + ":" + second$ + " GMT" return currentDateTime End Function ' Parse an HTTP date/time value, as specified in RFC 2616 sec 3.3 ' Preferred format is RFC1123, e.g., "Sun, 15 Mar 2016 08:15:10 GMT" Function ParseHTTPDateTime(httpDateTime As String) As Object months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] ' Currently, handle only RFC 1123 ' TODO - handle the other 'obsolete' HTTP formats, RFC 850/1036, and ANSI C asctime()format ' return current time if time string not valid systemTime = CreateObject("roSystemTime") dateTime = systemTime.GetUtcDateTime() dateTime.SetMillisecond(0) if mid(httpDateTime,4,1) = "," then day% = val(mid(httpDateTime,6,2)) if day% > 0 and day% < 32 then dateTime.SetDay(day%) endif httpMonth = mid(httpDateTime,9,3) for month% = 0 to 11 if months[month%] = httpMonth then dateTime.SetMonth(month% + 1) exit for endif next year% = val(mid(httpDateTime,13,4)) if year% > 0 and year% < dateTime.GetYear() then dateTime.SetYear(year%) endif hour% = val(mid(httpDateTime,18,2)) if hour% < 24 then dateTime.SetHour(hour%) endif minute% = val(mid(httpDateTime,21,2)) if minute% < 60 then dateTime.SetMinute(minute%) endif second% = val(mid(httpDateTime,24,2)) if second% < 60 then dateTime.SetSecond(second%) endif endif return dateTime End Function Function GetPhotoTimestamp(snapshotName$ As String) As String year$ = mid(snapshotName$, 1, 4) month$ = mid(snapshotName$, 5, 2) day$ = mid(snapshotName$, 7, 2) hour$ = mid(snapshotName$, 10, 2) minute$ = mid(snapshotName$, 12, 2) second$ = mid(snapshotName$, 14, 2) photoTimestamp$ = year$ + "-" + month$ + "-" + day$ + "T" + hour$ + "-" + minute$ + "-" + second$ + ".0000000Z" return photoTimestamp$ 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") ' queue live data feeds for downloading m.bsp.liveDataFeedsToDownload = [] for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) m.bsp.QueueRetrieveLiveDataFeed(m.liveDataFeeds, 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.bsp.QueueRetrieveLiveDataFeed(m.liveDataFeeds, 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.bsp.QueueRetrieveLiveDataFeed(m.liveDataFeeds, 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.bsp.QueueRetrieveLiveDataFeed(m.liveDataFeeds, 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() ' following line is commented out as the code should fall through to the next if statement ' return "HANDLED" endif endif endif next next else if event["EventType"] = "EXIT_SIGNAL" then m.bsp.diagnostics.PrintDebug(m.id$ + ": exit signal") endif if event["EventType"] = "MRSS_DATA_FEED_LOADED" or event["EventType"] = "CONTENT_DATA_FEED_LOADED" or event["EventType"] = "CONTENT_DATA_FEED_UNCHANGED" then m.bsp.AdvanceToNextLiveDataFeedInQueue(m.liveDataFeeds) endif endif endif if type(event) = "roTimerEvent" then eventIdentity$ = stri(event.GetSourceIdentity()) if m.bsp.liveDataFeedsByTimer.DoesExist(eventIdentity$) then liveDataFeed = m.bsp.liveDataFeedsByTimer.Lookup(eventIdentity$) ' if this feed's download failed, launch download of any pending feeds ' if this isn't done, no new downloads are ever retrieved as the queue is never empty and there's never an event that causes the ' next feed to be retrieved if lcase(type(liveDataFeed.lastDownloadedFailed)) = "roboolean" or lcase(type(liveDataFeed.lastDownloadedFailed)) = "boolean" then if liveDataFeed.lastDownloadedFailed then m.bsp.RetrievePendingLiveDataFeed(m.liveDataFeeds) endif endif ' requeue the feed that failed m.bsp.QueueRetrieveLiveDataFeed(m.liveDataFeeds, 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 updateInterval% = 0 headers = event.GetResponseHeaders() lastModifiedTime = invalid lastModifiedTimeStr = headers["Last-Modified"] if lastModifiedTimeStr <> invalid then lastModifiedTime = ParseHTTPDateTime(lastModifiedTimeStr) endif ' indicate that last download was successful liveDataFeed.lastDownloadedFailed = false if liveDataFeed.headRequest then getFeed = false if type(liveDataFeed.currentModifiedTime) <> "roDateTime" then ' retrieve the feed if there is no currentModifiedTime stamp for the feed m.bsp.diagnostics.PrintDebug("### Check live data feed - no current timestamp - updating") getFeed = true else headers = event.GetResponseHeaders() lastModifiedTimeStr = headers["Last-Modified"] if lastModifiedTimeStr <> invalid then lastModifiedTime = ParseHTTPDateTime(lastModifiedTimeStr) if lastModifiedTime <> liveDataFeed.currentModifiedTime then m.bsp.diagnostics.PrintDebug("### Check live data feed - updating '" + liveDataFeed.name$ + "', last modified = " + lastModifiedTime.GetString() + ", currentModified = " + liveDataFeed.currentModifiedTime.GetString()) ' retrieve the feed if there is a Last-Modified header and it is greater than the modified time of the current feed getFeed = true else m.bsp.diagnostics.PrintDebug("### Check live data feed - no update needed for '" + liveDataFeed.name$ + "', last modified = " + lastModifiedTime.GetString() + ", currentModified = " + liveDataFeed.currentModifiedTime.GetString()) endif else ' retrieve the feed if there is no Last-Modified header m.bsp.diagnostics.PrintDebug("### Check live data feed - no Last-Modified header - updating") getFeed = true endif endif if getFeed then liveDataFeed.headRequest = false m.bsp.RetrieveLiveDataFeed(m.liveDataFeeds, liveDataFeed) else ' No feed download needed, pop the item off the queue and see if something else is pending m.bsp.AdvanceToNextLiveDataFeedInQueue(m.liveDataFeeds) ' Send next HEAD request after interval updateInterval% = liveDataFeed.updateInterval% endif else userVariables = m.bsp.currentUserVariables liveDataFeed.currentModifiedTime = lastModifiedTime if liveDataFeed.usage$ <> "mrss" and liveDataFeed.usage$ <> "mrsswith4k" then ' simple RSS or content if liveDataFeed.usage$ = "content" and (liveDataFeed.isDynamicPlaylist or liveDataFeed.isLiveMediaFeed) then liveDataFeed.ParseMRSSFeed(liveDataFeed.rssFileName$) liveDataFeed.ConvertMRSSFormatToContent() else liveDataFeed.ParseSimpleRSSFeed(liveDataFeed.rssFileName$) 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 else ' These must be valid objects even for MRSS feeds (at least for now) liveDataFeed.articles = CreateObject("roArray", 1, true) liveDataFeed.articleTitles = CreateObject("roArray", 1, true) liveDataFeed.articlesByTitle = CreateObject("roAssociativeArray") endif liveDataFeed.isMRSSFeed = liveDataFeed.FeedIsMRSS( liveDataFeed.rssFileName$ ) if liveDataFeed.usage$ = "content" then liveDataFeed.DownloadLiveFeedContent() else if liveDataFeed.usage$ = "mrss" or liveDataFeed.usage$ = "mrsswith4k" and (liveDataFeed.parser$ <> "" or 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 if liveDataFeed.useHeadRequest then ' Set headRequest so that next call will be a HEAD call liveDataFeed.headRequest = true endif updateInterval% = liveDataFeed.updateInterval% end if 'feed download, not HEAD else 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) ' remove the failed feed from the queue in case there's a problem with the feed, but only if it was on the queue if liveDataFeed.usage$ <> "text" then m.bsp.RemoveFailedFeedFromQueue() endif ' start a timer before attempting to retrieve the next feed in case there's a problem with the network updateInterval% = m.stateMachine.dataFeedRetryInterval% ' indicate that last download failed liveDataFeed.lastDownloadedFailed = true endif ' set a timer to update this 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") ok = m.stateMachine.currentSync.ReadFromFile("local-sync.xml") or m.stateMachine.currentSync.ReadFromFile("localSetupToStandalone-sync.xml") if not ok 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) m.assetFetcher.AddHeader("User-Agent",m.bsp.userAgent$) 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, "AssetPool Protect Failure") m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + "AssetPool Protect Failure") 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, "AssetPool Protect Failure") m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + "AssetPool Protect Failure") 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 RemoveFailedFeedFromQueue() ' remove failed feed - it will get added back to queue when retry timeout occurs failedFeed = m.liveDataFeedsToDownload.Shift() End Sub Sub RetrievePendingLiveDataFeed(liveDataFeeds As Object) if m.liveDataFeedsToDownload.Count() > 0 then m.RetrieveLiveDataFeed(liveDataFeeds, m.liveDataFeeds[m.liveDataFeedsToDownload[0]]) endif End Sub Sub QueueRetrieveLiveDataFeed(liveDataFeeds As Object, liveDataFeed As Object) ' for download feeds that are neither MRSS nor content immediately (simple RSS) if liveDataFeed.usage$ = "text" then m.RetrieveLiveDataFeed(liveDataFeeds, liveDataFeed) else m.liveDataFeedsToDownload.push(liveDataFeed.name$) ' launch download of first feed if m.liveDataFeedsToDownload.Count() = 1 then m.RetrieveLiveDataFeed(liveDataFeeds, liveDataFeed) endif endif End Sub Sub AdvanceToNextLiveDataFeedInQueue(liveDataFeeds as Object) ' Remove top entry m.liveDataFeedsToDownload.Shift() if m.liveDataFeedsToDownload.Count() > 0 then liveDataFeedName = m.liveDataFeedsToDownload[0] liveDataFeed = m.liveDataFeeds.Lookup(liveDataFeedName) m.RetrieveLiveDataFeed(liveDataFeeds, liveDataFeed) endif End Sub Sub RetrieveLiveDataFeed(liveDataFeeds As Object, liveDataFeed As Object) url$ = liveDataFeed.url.GetCurrentParameterValue() auth = liveDataFeed.authenticationData if liveDataFeed.headRequest then m.diagnostics.PrintDebug("### Checking live text data feed from " + url$) m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_CHECK_LIVE_TEXT_FEED_HEAD, url$) else m.diagnostics.PrintDebug("### Retrieve live text data feed from " + url$) m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_RETRIEVE_LIVE_TEXT_FEED, url$) endif liveDataFeed.rssURLXfer = CreateObject("roUrlTransfer") liveDataFeed.rssURLXfer.SetUrl(url$) liveDataFeed.rssURLXfer.SetPort(m.msgPort) if not liveDataFeed.headRequest then liveDataFeed.rssFileName$ = m.GetRSSTempFilename() end if liveDataFeed.rssURLXfer.SetTimeout(55000) ' 55 second timeout ' Set User agent string - see in there is a custom parser function for the user agent userAgent$ = "" if liveDataFeed.customUserAgent$ <> "" then data = {userAgent: m.userAgent$} retVal = Eval(liveDataFeed.customUserAgent$ + "(m, data)") ERR_NORMAL_END = &hFC if retVal = ERR_NORMAL_END then if IsString(data.userAgent) then userAgent$ = data.userAgent m.diagnostics.PrintDebug("Using custom user agent string for " + liveDataFeed.name$ + ": "+ userAgent$) endif else ' log the failure m.diagnostics.PrintDebug("Failure invoking Eval to parse custom User Agent for data feed: return value = " + stri(retVal) + ", parser is " + liveDataFeed.customUserAgent$) m.logging.WriteDiagnosticLogEntry(m.diagnosticCodes.EVENT_CUSTOM_USER_AGENT_FAILURE, stri(retVal) + chr(9) + liveDataFeed.customUserAgent$) endif endif if userAgent$ <> "" then liveDataFeed.rssURLXfer.SetUserAgent(userAgent$) else liveDataFeed.rssURLXfer.SetUserAgent(m.userAgent$) endif ' 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.diagnostics.PrintDebug("Failed to set authorization header, reason: " + liveDataFeed.rssURLXfer.GetFailureReason()) endif endif endif binding% = GetBinding(m.textFeedsXfersEnabledWired, m.textFeedsXfersEnabledWireless) m.diagnostics.PrintDebug("### Binding for RetrieveLiveDataFeed is " + stri(binding%)) ok = liveDataFeed.rssURLXfer.BindToInterface(binding%) if not ok then stop if liveDataFeed.headRequest then liveDataFeed.rssURLXfer.AsyncHead() else liveDataFeed.rssURLXfer.AsyncGetToFile(liveDataFeed.rssFileName$) end if 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.deviceDownloadProgressUploadURL = invalid NetworkingStateMachine.deviceDownloadUploadURL = invalid NetworkingStateMachine.trafficDownloadUploadURL = invalid NetworkingStateMachine.mrssTrafficDownloadUploadURL = invalid NetworkingStateMachine.eventUploadURL = invalid NetworkingStateMachine.deviceErrorUploadURL = 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.ParseAWSURLs = ParseAWSURLs NetworkingStateMachine.SetRemoteSnapshotUrls = SetRemoteSnapshotUrls NetworkingStateMachine.SetBASParameters = SetBASParameters 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.QueueSnapshotForBSN = QueueSnapshotForBSN NetworkingStateMachine.stNetworkScheduler.UploadSnapshotToBSN = UploadSnapshotToBSN NetworkingStateMachine.stNetworkScheduler.UploadSnapshotToBSNEE = UploadSnapshotToBSNEE NetworkingStateMachine.stNetworkScheduler.UploadSnapshotToSFN = UploadSnapshotToSFN NetworkingStateMachine.stNetworkScheduler.BuildStringToSign = BuildStringToSign 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.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 ' remote snapshot values m.SetRemoteSnapshotUrls(m.currentSync) m.pendingSnapshotsToUpload = {} ' 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$ else if dwsParams.dwsEnabled$ = "no" then dwsAA["port"] = 0 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 m.bsp.obfuscatedEncryptionKey = "" return m.stRetrievingSyncList End Function Sub SetRemoteSnapshotUrls(syncSpec As Object) m.deviceScreenShotsTemporaryStorage$ = syncSpec.LookupMetadata("client", "deviceScreenShotsTemporaryStorage") m.incomingDeviceScreenshotsQueue$ = syncSpec.LookupMetadata("client", "incomingDeviceScreenshotsQueue") m.awsAccessKeyId$ = syncSpec.LookupMetadata("client", "awsAccessKeyId") m.awsSecretAccessKey$ = syncSpec.LookupMetadata("client", "awsSecretAccessKey") m.awsSessionToken$ = syncSpec.LookupMetadata("client", "awsSessionToken") m.ParseAWSURLs() m.uploadSnapshotsURL$ = syncSpec.LookupMetadata("client", "uploadsnapshots") m.uploadDeviceScreenshotHandlerAddress = syncSpec.LookupMetadata("client", "uploadDeviceScreenshotHandlerAddress") m.securityToken = syncSpec.LookupMetadata("client", "securityToken") End Sub Sub ParseAWSURLs() m.AwsBucketName = "" ' bsnm2 m.AwsBaseAddress = "" ' https://s3.amazonaws.com/ m.AwsIncomingDirectory = "" ' DeviceScreenShots/Incoming/ m.AwsSqsHost = "" ' sqs.us-east-1.amazonaws.com m.AwsSqsAbsolutePath = "" ' /965175186373/bsn-QA-RS-IDSS m.AwsSqsService = "" ' sqs m.AwsSqsRegion = "" ' us-east-1 if m.deviceScreenShotsTemporaryStorage$ <> "" then regexSlash = CreateObject("roRegEx","/","i") regexDot = CreateObject("roRegEx","\.","i") urlItems = regexSlash.Split(m.deviceScreenShotsTemporaryStorage$) hostItems = regexDot.Split(urlItems[2]) sqsUrlItems = regexSlash.Split(m.incomingDeviceScreenshotsQueue$) ' Get Bucket m.AwsBucketName = hostItems[0] ' Get Base Address m.AwsBaseAddress = urlItems[0] + "//" + hostItems[1] for i = 2 to (hostItems.Count() - 1) m.AwsBaseAddress = m.AwsBaseAddress + "." + hostItems[i] next m.AwsBaseAddress = m.AwsBaseAddress + "/" ' Get Resource Key m.AwsIncomingDirectory = "" for i = 3 to (urlItems.Count() - 1) m.AwsIncomingDirectory = m.AwsIncomingDirectory + urlItems[i] + "/" next ' sqs url host m.AwsSqsHost = sqsUrlItems[2] ' sqs absolute path for i = 3 to (urlItems.Count() - 1) m.AwsSqsAbsolutePath = m.AwsSqsAbsolutePath + "/" + sqsUrlItems[i] next ' sqs service,region sqsHostItems = regexDot.Split(m.AwsSqsHost) m.AwsSqsService = sqsHostItems[0] m.AwsSqsRegion = sqsHostItems[1] endif End Sub 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 = {} m.chargeableFiles = {} 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) m.deviceDownloadProgressUploadURL.SetUserAgent(m.bsp.userAgent$) 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) m.deviceDownloadProgressUploadURL.SetUserAgent(m.bsp.userAgent$) 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 ' create progress items for each file in each feed for each liveDataFeedName in m.bsp.liveDataFeeds liveDataFeed = m.bsp.liveDataFeeds.Lookup(liveDataFeedName) for each fileToDownloadKey in liveDataFeed.feedContentFilesToDownload fileToDownload = liveDataFeed.feedContentFilesToDownload.Lookup(fileToDownloadKey) if type(fileToDownload) = "roAssociativeArray" then m.PushDeviceDownloadProgressItem(fileToDownload, "fileInSyncSpec", fileToDownload.currentFilePercentage$, fileToDownload.status$) endif next 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) m.deviceDownloadUploadURL.SetUserAgent(m.bsp.userAgent$) 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) m.uploadLogFileURLXfer.SetUserAgent(m.bsp.userAgent$) 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) m.trafficDownloadUploadURL.SetUserAgent(m.bsp.userAgent$) 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) m.mrssTrafficDownloadUploadURL.SetUserAgent(m.bsp.userAgent$) 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 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) m.eventUploadURL.SetUserAgent(m.bsp.userAgent$) 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) m.deviceErrorUploadURL.SetUserAgent(m.bsp.userAgent$) 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" else if event["EventType"] = "SNAPSHOT_CAPTURED" then snapshotName$ = event["SnapshotName"] if type(m.stateMachine.awsAccessKeyId$) = "roString" and m.stateMachine.awsAccessKeyId$ <> "" then m.UploadSnapshotToBSN(snapshotName$) else if type(m.stateMachine.securityToken) = "roString" and m.stateMachine.securityToken <> "" then m.UploadSnapshotToBSNEE(snapshotName$) else if m.stateMachine.uploadSnapshotsURL$ <> "" then m.UploadSnapshotToSFN(snapshotName$) endif else if event["EventType"] = "SEND_HEARTBEAT" then m.stateMachine.SendHeartbeat() 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 if type(m.stateMachine.retrySnapshotUploadTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.stateMachine.retrySnapshotUploadTimer.GetIdentity()) then if type(m.stateMachine.uploadSnapshotUrl) = "roUrlTransfer" then ' a snapshot upload is currently in progress - return and wait for completion event handler else ' make sure there's an outstanding snapshot to upload if not m.stateMachine.pendingSnapshotsToUpload.IsEmpty() then m.stateMachine.pendingSnapshotsToUpload.Reset() snapshotName = m.stateMachine.pendingSnapshotsToUpload.Next() m.stateMachine.pendingSnapshotsToUpload.Delete(snapshotName) m.UploadSnapshotToBSN(snapshotName) endif endif m.stateMachine.retrySnapshotUploadTimer = invalid return "HANDLED" endif endif if type(m.bsp.overrideBSNTimer) = "roTimer" then if stri(event.GetSourceIdentity()) = stri(m.bsp.overrideBSNTimer.GetIdentity()) then m.bsp.diagnostics.PrintDebug("### BSN override expired.") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BSN_OVERRIDE_EXPIRED, "") m.bsp.overrideBSNTimer = invalid m.stateMachine.SetBASParameters(m.stateMachine.currentSync, false, false, true, true) endif endif else if type(event) = "roUrlEvent" then if type(m.stateMachine.uploadSnapshotToBSNEEUrl) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.uploadSnapshotToBSNEEUrl.GetIdentity() then if event.GetResponseCode() = 200 then m.bsp.diagnostics.PrintDebug("### Snapshot file uploaded to BSNEE") else ' log failure m.bsp.diagnostics.PrintDebug("### snapshot upload failure " + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_UPLOAD_ERROR, stri(event.GetResponseCode()) + " " + event.GetFailureReason()) endif m.stateMachine.uploadSnapshotToBSNEEUrl = invalid return "HANDLED" endif endif if type(m.stateMachine.uploadSnapshotToSFNUrl) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.uploadSnapshotToSFNUrl.GetIdentity() then if event.GetResponseCode() = 200 then m.bsp.diagnostics.PrintDebug("### snapshot sucessfully uploaded to simple file networking handler: " + m.stateMachine.uploadSnapshotsURL$) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_UPLOADED, " ") else m.bsp.diagnostics.PrintDebug("### snapshot upload failure " + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_UPLOAD_ERROR, stri(event.GetResponseCode()) + " " + event.GetFailureReason()) endif endif endif if type(m.stateMachine.uploadSnapshotUrl) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.uploadSnapshotUrl.GetIdentity() then snapshot = m.stateMachine.uploadSnapshotUrl.GetUserData() snapshotName$ = snapshot.name url = snapshot.url if event.GetResponseCode() = 200 then ' note - don't check for prior upload failures here - go ahead and queue this upload so that users can see ' this latest snapshots as soon as possible. m.QueueSnapshotForBSN(snapshotName$, url) else ' log failure m.bsp.diagnostics.PrintDebug("### snapshot upload failure " + snapshotName$ + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_UPLOAD_ERROR, snapshotName$ + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) ' retry m.stateMachine.pendingSnapshotsToUpload.AddReplace(snapshotName$, snapshotName$) if type(m.stateMachine.retrySnapshotUploadTimer) <> "roTimer" then m.stateMachine.retrySnapshotUploadTimer = CreateObject("roTimer") m.stateMachine.retrySnapshotUploadTimer.SetPort(m.stateMachine.msgPort) m.stateMachine.retrySnapshotUploadTimer.SetElapsed(30, 0) m.stateMachine.retrySnapshotUploadTimer.Start() endif endif ' indicate that transfer is no longer in progress m.stateMachine.uploadSnapshotUrl = invalid return "HANDLED" endif endif if type(m.stateMachine.queueSnapshotUrl) = "roUrlTransfer" then if event.GetSourceIdentity() = m.stateMachine.queueSnapshotUrl.GetIdentity() then snapshotName$ = m.stateMachine.queueSnapshotUrl.GetUserData() if event.GetResponseCode() = 200 then m.bsp.diagnostics.PrintDebug("### snapshot uploaded and queued" + snapshotName$) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_UPLOADED_AND_QUEUED, snapshotName$) ' if prior uploads failed and another upload is not in progress, retry upload if not m.stateMachine.pendingSnapshotsToUpload.IsEmpty() and type(m.stateMachine.uploadSnapshotUrl) <> "roUrlTransfer" then m.stateMachine.pendingSnapshotsToUpload.Reset() snapshotName = m.stateMachine.pendingSnapshotsToUpload.Next() m.stateMachine.pendingSnapshotsToUpload.Delete(snapshotName) m.UploadSnapshotToBSN(snapshotName) endif else ' queue operation failed - retry entire upload / queue sequence ' log failure m.bsp.diagnostics.PrintDebug("### snapshot queue failure " + snapshotName$ + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_SCREENSHOT_QUEUE_ERROR, snapshotName$ + stri(event.GetResponseCode()) + " " + event.GetFailureReason()) m.stateMachine.pendingSnapshotsToUpload.AddReplace(snapshotName$, snapshotName$) if type(m.stateMachine.retrySnapshotUploadTimer) <> "roTimer" then m.stateMachine.retrySnapshotUploadTimer = CreateObject("roTimer") m.stateMachine.retrySnapshotUploadTimer.SetPort(m.stateMachine.msgPort) m.stateMachine.retrySnapshotUploadTimer.SetElapsed(30, 0) m.stateMachine.retrySnapshotUploadTimer.Start() endif endif return "HANDLED" endif endif 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.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.SetUserAgent(m.bsp.userAgent$) m.sendHeartbeatUrl.SetUrl(m.heartbeatURL$) m.sendHeartbeatUrl.SetTimeout(90000) ' 90 second timeout ' Add minimum number of headers m.sendHeartbeatUrl.SetHeaders(m.currentSync.GetMetadata("server")) m.sendHeartbeatUrl.AddHeader("BrightWallName", m.bsp.registrySettings.brightWallName$) m.sendHeartbeatUrl.AddHeader("BrightWallScreenNumber", m.bsp.registrySettings.brightWallScreenNumber$) 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.xfer.SetUserAgent(m.bsp.userAgent$) 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 associated with remote snapshot - TODO - shouldn't have to do this!! m.xfer.AddHeader("deviceScreenShotsEnabled", m.stateMachine.currentSync.LookupMetadata("client", "deviceScreenShotsEnabled")) m.xfer.AddHeader("deviceScreenShotsInterval", m.stateMachine.currentSync.LookupMetadata("client", "deviceScreenShotsInterval")) m.xfer.AddHeader("deviceScreenShotsCountLimit", m.stateMachine.currentSync.LookupMetadata("client", "deviceScreenShotsCountLimit")) m.xfer.AddHeader("deviceScreenShotsQuality", m.stateMachine.currentSync.LookupMetadata("client", "deviceScreenShotsQuality")) ' globalAA = GetGlobalAA() ' if globalAA.enableRemoteSnapshot then ' deviceScreenShotsEnabled$ = "yes" ' deviceScreenShotsInterval$ = StripLeadingSpaces(stri(globalAA.remoteSnapshotInterval)) ' deviceScreenShotsCountLimit$ = StripLeadingSpaces(stri(globalAA.remoteSnapshotMaxImages)) ' deviceScreenShotsQuality$ = StripLeadingSpaces(stri(globalAA.remoteSnapshotJpegQualityLevel)) ' else ' deviceScreenShotsEnabled$ = "no" ' deviceScreenShotsInterval$ = "" ' deviceScreenShotsCountLimit$ = "" ' deviceScreenShotsQuality$ = "" ' endif ' m.xfer.AddHeader("deviceScreenShotsEnabled", deviceScreenShotsEnabled$) ' m.xfer.AddHeader("deviceScreenShotsInterval", deviceScreenShotsInterval$) ' m.xfer.AddHeader("deviceScreenShotsCountLimit", deviceScreenShotsCountLimit$) ' m.xfer.AddHeader("deviceScreenShotsQuality", deviceScreenShotsQuality$) ' Add headers for BrightWall m.xfer.AddHeader("BrightWallName", m.bsp.registrySettings.brightWallName$) m.xfer.AddHeader("BrightWallScreenNumber", m.bsp.registrySettings.brightWallScreenNumber$) ' 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("variableLoggingEnabled", m.bsp.registrySettings.variableLoggingEnabled) 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("gateway", m.bsp.registrySection.Read("gw")) xfer.AddHeader("broadcast", 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_2$) if useDHCP_2$ = "no" then xfer.AddHeader("staticIPAddress_2", m.bsp.registrySection.Read("sip2")) xfer.AddHeader("subnetMask_2", m.bsp.registrySection.Read("sm2")) xfer.AddHeader("gateway_2", m.bsp.registrySection.Read("gw2")) xfer.AddHeader("broadcast_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") ' save last successful connection to BSN in local time currentTime = m.bsp.systemTime.GetLocalDateTime() m.bsp.WriteRegistrySetting("lastBSNConnectionTime", currentTime) m.bsp.registrySettings.lastBSNConnectionTime = currentTime headers=event.getResponseHeaders() if headers.DoesExist("bsn-content-passphrase") and m.bsp.contentEncryptionSupported then m.bsp.obfuscatedEncryptionKey = headers["bsn-content-passphrase"] else m.bsp.obfuscatedEncryptionKey = "" endif ' 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 ' Determine if device is in BrightWall group, then clear brightWall settings in registry if they are currently set but device is no longer part of BrightWall group if m.bsp.registrySettings.brightWallName$ <> "" and m.bsp.registrySettings.brightWallScreenNumber$ <> "" then clearBrightWallSettings = false serverMetadata = m.stateMachine.newSync.GetMetadata("server") if type(serverMetadata) = "roAssociativeArray" then if not serverMetadata.DoesExist("brightWall") or serverMetadata["brightWall"] = "" then clearBrightWallSettings = true endif endif if clearBrightWallSettings then m.bsp.registrySection.Write("brightWallName", "") m.bsp.registrySection.Write("brightWallScreenNumber", "") m.bsp.registrySection.Flush() m.bsp.registrySettings.brightWallName$ = "" m.bsp.registrySettings.brightWallScreenNumber$ = "" endif 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 ) ' Only update values that the application web server can change if not currently in an override state. if type(m.bsp.overrideBSNTimer) = "roTimer" then overrideModeActive = true else overrideModeActive = false endif m.stateMachine.SetBASParameters(m.stateMachine.newSync, true, true, not overrideModeActive, not overrideModeActive) ' 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 variableLoggingEnabled = false b$ = LCase(m.statemachine.newSync.LookupMetadata("client", "variableLoggingEnabled")) if b$ = "yes" then variableLoggingEnabled = 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, variableLoggingEnabled, uploadLogFilesAtBoot, uploadLogFilesAtSpecificTime, uploadLogFilesTime%) ' Retrieve unit name, description from sync spec and write to registry if they have changed ' parameters are not set in SFN case, so don't change them here if m.statemachine.newSync.GetName() <> "Simple Networking" then 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 proxyFromSyncSpec$ = m.stateMachine.newSync.LookupMetadata("client", "proxy") if proxyFromSyncSpec$ <> "" then useProxy$ = "yes" else useProxy$ = "no" endif useProxy$ = m.UpdateRegistrySetting(useProxy$, m.bsp.registrySettings.useProxy, "up") proxySpec$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "proxy"), m.bsp.registrySettings.proxy$, "ps") nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then ok = nc.SetProxy(proxySpec$) nc.Apply() endif nc = CreateObject("roNetworkConfiguration", 1) if type(nc) = "roNetworkConfiguration" then ok = nc.SetProxy(proxySpec$) nc.Apply() endif nc = invalid networkHosts$ = m.UpdateRegistrySetting(m.stateMachine.newSync.LookupMetadata("client", "networkHosts"), m.bsp.registrySettings.networkHosts$, "bph") networkHosts = ParseJSON(networkHosts$) bypassProxyHosts = [] for each networkHost in networkHosts if networkHost.HostName <> "" then bypassProxyHosts.push(networkHost.HostName) endif next ' bypass proxy servers if m.bsp.bypassProxySupported then nc = CreateObject("roNetworkConfiguration", 0) if type(nc) = "roNetworkConfiguration" then ok = nc.SetProxyBypass(bypassProxyHosts) nc.Apply() endif nc = CreateObject("roNetworkConfiguration", 1) if type(nc) = "roNetworkConfiguration" then ok = nc.SetProxyBypass(bypassProxyHosts) nc.Apply() endif nc = invalid endif 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 persistent beacon data, and write to registry and update beacons if changed beacon1Data = m.stateMachine.newSync.LookupMetadata("client", "beacon1") beacon2Data = m.stateMachine.newSync.LookupMetadata("client", "beacon2") beaconsChanged = false beacon1FromRegistry$ = m.bsp.registrySettings.beacon1 if beacon1Data <> beacon1FromRegistry$ then m.bsp.WriteRegistrySetting("beacon1", beacon1Data) m.bsp.registrySettings.beacon1 = beacon1Data beaconsChanged = true endif beacon2FromRegistry$ = m.bsp.registrySettings.beacon2 if beacon2Data <> beacon2FromRegistry$ then m.bsp.WriteRegistrySetting("beacon2", beacon2Data) m.bsp.registrySettings.beacon2 = beacon2Data beaconsChanged = true endif if beaconsChanged then ' Update beacons with changed data and restart m.bsp.btManager.UpdatePersistentBeacons() 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 if event.GetResponseCode() <> 503 then ' retry - server returned something other than a 200, a 404 or a 503 m.stateMachine.ResetDownloadTimerToDoRetry() if event.GetFailureReason() <> "" then eventData$ = event.GetFailureReason() else eventData$ = str(event.GetResponseCode()) endif m.stateMachine.logging.WriteDiagnosticLogEntry(m.stateMachine.diagnosticCodes.EVENT_RETRIEVE_SYNCSPEC_FAILURE, eventData$) m.bsp.diagnostics.PrintDebug("### Failed to download sync list: " + eventData$) m.stateMachine.AddDeviceErrorItem("deviceError", "", "Failed to download sync list: " + event.GetFailureReason(), str(event.GetResponseCode())) else print "received 503" endif endif return m.stateMachine.stWaitForTimeout End Function Function NetworkingIsActive() return m.networkingActive End Function ' update parameters that can be set by BrightSign application server ' either update them when no override is active, or when the override expires Sub SetBASParameters(syncSpec As Object, setURLSFromSyncSpec As Boolean, setRegistryFromSyncSpec As Boolean, setGlobalsFromRegistry As Boolean, updateRunTime As Boolean) globalAA = GetGlobalAA() registrySection = CreateObject("roRegistrySection", "networking") if type(registrySection)<>"roRegistrySection" then print "Error: Unable to create roRegistrySection":stop if setURLSFromSyncSpec then m.SetRemoteSnapshotUrls(syncSpec) endif if setRegistryFromSyncSpec then registrySection.Write("enableRemoteSnapshot", syncSpec.LookupMetadata("client", "deviceScreenShotsEnabled")) registrySection.Write("remoteSnapshotInterval", syncSpec.LookupMetadata("client", "deviceScreenShotsInterval")) registrySection.Write("remoteSnapshotMaxImages", syncSpec.LookupMetadata("client", "deviceScreenShotsCountLimit")) registrySection.Write("remoteSnapshotJpegQualityLevel", syncSpec.LookupMetadata("client", "deviceScreenShotsQuality")) registrySection.Write("remoteSnapshotDisplayPortrait", syncSpec.LookupMetadata("client", "deviceScreenShotsDisplayPortrait")) endif ' remember current settings currentEnableRemoteSnapshot = globalAA.enableRemoteSnapshot currentRemoteSnapshotInterval = globalAA.remoteSnapshotInterval currentRemoteSnapshotMaxImages = globalAA.remoteSnapshotMaxImages currentRemoteSnapshotJpegQualityLevel = globalAA.remoteSnapshotJpegQualityLevel currentRemoteSnapshotDisplayPortrait = globalAA.remoteSnapshotDisplayPortrait if setGlobalsFromRegistry then globalAA.enableRemoteSnapshot = GetBoolFromString(registrySection.Read("enableRemoteSnapshot"), false) globalAA.remoteSnapshotInterval = int(val(registrySection.Read("remoteSnapshotInterval"))) globalAA.remoteSnapshotMaxImages = int(val(registrySection.Read("remoteSnapshotMaxImages"))) globalAA.remoteSnapshotJpegQualityLevel = int(val(registrySection.Read("remoteSnapshotJpegQualityLevel"))) globalAA.remoteSnapshotDisplayPortrait = GetBoolFromString(registrySection.Read("remoteSnapshotDisplayPortrait"), false) endif if updateRunTime then if currentEnableRemoteSnapshot <> globalAA.enableRemoteSnapshot then if globalAA.enableRemoteSnapshot then ' remote snapshots were off; turn them on m.bsp.remoteSnapshotTimer = CreateObject("roTimer") m.bsp.remoteSnapshotTimer.SetPort(m.bsp.msgPort) m.bsp.remoteSnapshotTimer.SetElapsed(m.bsp.globalAA.remoteSnapshotInterval, 0) m.bsp.remoteSnapshotTimer.Start() else ' remote snapshots were on; turn them off if type(m.remoteSnapshotTimer) = "roTimer" then m.remoteSnapshotTimer.Stop() endif endif endif endif End Sub 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.SetIP4Gateway(networkingParameters.gateway$) if m.stateMachine.bsp.setDNSServersSupported then dnsServers = [] if networkingParameters.dns1$ <> "" then dnsServers.push(networkingParameters.dns1$) endif if networkingParameters.dns2$ <> "" then dnsServers.push(networkingParameters.dns2$) endif if networkingParameters.dns3$ <> "" then dnsServers.push(networkingParameters.dns3$) endif if dnsServers.Count() > 0 then ok = nc.SetDNSServers([]) ok = nc.SetDNSServers(dnsServers) endif else if networkingParameters.dns1$ <> "" then nc.AddDNSServer(networkingParameters.dns1$) if networkingParameters.dns2$ <> "" then nc.AddDNSServer(networkingParameters.dns2$) if networkingParameters.dns3$ <> "" then nc.AddDNSServer(networkingParameters.dns3$) endif else nc.SetDHCP() endif nc.SetRoutingMetric(int(val(networkingParameters.networkConnectionPriority$))) if networkingParameters.useWireless then wpaEnabled = registryNetworkingParameters.enableWPAEnterpriseAuthentication or registryNetworkingParameters.enableWPAEnterpriseAuthentication2 if not wpaEnabled then ' Don't overwrite the eap or peap passphrase, if WPA is in use. ' This is necessary because BSN and this autorun doesn't support WPA. nc.SetWiFiESSID(networkingParameters.ssid$) nc.SetObfuscatedWifiPassphrase(networkingParameters.passphrase$) endif 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(100,60) m.stateMachine.assetFetcher.SetHeaders(m.stateMachine.newSync.GetMetadata("server")) m.stateMachine.assetFetcher.AddHeader("User-Agent", m.bsp.userAgent$) 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$ else if dwsParams.dwsEnabled$ = "no" then dwsAA["port"] = 0 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) m.bsp.contentEncrypted = false if m.bsp.contentEncryptionSupported then deviceCustomization = CreateObject("roDeviceCustomization") deviceCustomization.StoreObfuscatedEncryptionKey("AesCtrHmac", m.bsp.obfuscatedEncryptionKey) if m.bsp.obfuscatedEncryptionKey <> "" then m.bsp.contentEncrypted = true endif endif m.bsp.SetPerFileEncryptionStatus(m.stateMachine.currentSync) ' 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, "AssetPool Protect Failure") m.stateMachine.logging.FlushLogFile() DeleteFile("autorun.brs") m.bsp.diagnostics.PrintDebug("### ProtectFiles failed: " + "AssetPool Protect Failure") m.stateMachine.AddDeviceErrorItem("deviceError", m.stateMachine.currentSync.GetName(), "ProtectFilesFailure: " + "AssetPool Protect Failure", "") 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 IsControlPort(controlPort) then if controlPort.IsInputActive(buttonNumber%) then GPIOStateMachine.state$ = "ButtonDown" else GPIOStateMachine.state$ = "ButtonUp" endif 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)) if type(msg) = "roControlDown" and stri(msg.GetSourceIdentity()) = stri(m.svcPort.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 3 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 m.diagnostics.DiagnoseAndRecoverWifiNetwork(msg) 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" and not m.sign.enableEnhancedSynchronization 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$) if IsControlPort(m.controlPort) then m.controlPort.SetOutputState(int(val(gpioNumber$)), 1) endif else if command$ = "gpioOffCommand" then gpioNumberParameter = parameters["gpioNumber"] gpioNumber$ = gpioNumberParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Turn off gpioNumber " + gpioNumber$) if IsControlPort(m.controlPort) then m.controlPort.SetOutputState(int(val(gpioNumber$)), 0) endif else if command$ = "gpioSetStateCommand" then gpioStateParameter = parameters["stateValue"] gpioState$ = gpioStateParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("Set GPIO's to " + gpioState$) if IsControlPort(m.controlPort) then m.controlPort.SetWholeState(int(val(gpioState$))) endif 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" or type(serial) = "roUsbTap" 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" or type(serial) = "roUsbTap" 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" or type(serial) = "roUsbTap" 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" or type(serial) = "roUsbTap" 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$ = "sendUDPBytesCommand" then byteValueParameter = parameters["byteValues"] byteValues$ = byteValueParameter.GetCurrentParameterValue() m.diagnostics.PrintDebug("sendUDPBytesCommand " + byteValues$) ba = CreateObject("roByteArray") byteString$ = StripLeadingSpaces(byteValues$) if len(byteString$) > 0 then commaPosition = -1 while commaPosition <> 0 commaPosition = instr(1, byteString$, ",") ba.push(val(byteString$)) byteString$ = mid(byteString$, commaPosition+1) end while endif m.diagnostics.PrintDebug("Send UDP command " + ba.ToHexString()) m.udpSender.Send(ba) 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 then m.diagnostics.PrintDebug("Send synchronize command " + synchronizeKeyword$ + " using SyncManager.") syncManagerEvent = m.SyncManager.Synchronize(synchronizeKeyword$, 300) m.diagnostics.PrintDebug("@@@ Created syncManagerEvent with sync keyword: " + synchronizeKeyword$) 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.mjpegvideoPlayer) = "roVideoPlayer" then zone.mjpegVideoPlayer.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 if type(zone.canvasWidget) = "roCanvasWidget" then zone.canvasWidget.SetRectangle(r) endif if type(zone.widget) = "roClockWidget" or type(zone.widget) = "roTextWidget" then ok = zone.widget.SetRectangle(r) endif endif else if command$ = "hideZone" then m.diagnostics.PrintDebug("Execute hideZone command") zoneId$ = parameters["zone"].GetCurrentParameterValue() zoneHSM = m.GetZone(zoneId$) if type(zoneHSM) = "roAssociativeArray" then if not zoneHSM.isVisible return if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then if m.showHideVideoZoneSupported then if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Hide() endif if type(zoneHSM.imagePlayer) <> "Invalid" then zoneHSM.imagePlayer.Hide() if type(zoneHSM.canvasWidget) <> "Invalid" then zoneHSM.canvasWidget.Hide() if type(zoneHSM.loadingHtmlWidget) <> "Invalid" then zoneHSM.loadingHtmlWidget.Hide() if type(zoneHSM.displayedHtmlWidget) <> "Invalid" then zoneHSM.displayedHtmlWidget.Hide() else if zoneHSM.type$ = "VideoOnly" then if m.showHideVideoZoneSupported then if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Hide() endif else if zoneHSM.type$ = "Clock" then zoneHSM.widget.Hide() else if zoneHSM.type$ = "Ticker" then zoneHSM.widget.Hide() else if zoneHSM.type$ = "BackgroundImage" then endif zoneHSM.isVisible = false endif else if command$ = "showZone" then m.diagnostics.PrintDebug("Execute showZone command") zoneId$ = parameters["zone"].GetCurrentParameterValue() zoneHSM = m.GetZone(zoneId$) if type(zoneHSM) = "roAssociativeArray" then if zoneHSM.isVisible return if zoneHSM.type$ = "VideoOrImages" or zoneHSM.type$ = "Images" then if m.showHideVideoZoneSupported then if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Show() endif if type(zoneHSM.imagePlayer) <> "Invalid" and not zoneHSM.imageHidden then zoneHSM.imagePlayer.Show() if type(zoneHSM.canvasWidget) <> "Invalid" and not zoneHSM.canvasHidden then zoneHSM.canvasWidget.Show() if type(zoneHSM.loadingHtmlWidget) <> "Invalid" and not zoneHSM.htmlHidden then zoneHSM.loadingHtmlWidget.Show() if type(zoneHSM.displayedHtmlWidget) <> "Invalid" and not zoneHSM.htmlHidden then zoneHSM.displayedHtmlWidget.Show() else if zoneHSM.type$ = "VideoOnly" then if m.showHideVideoZoneSupported then if type(zoneHSM.videoPlayer) = "roVideoPlayer" then zoneHSM.videoPlayer.Show() endif else if zoneHSM.type$ = "Clock" then zoneHSM.widget.Show() else if zoneHSM.type$ = "Ticker" then zoneHSM.widget.Show() else if zoneHSM.type$ = "BackgroundImage" then endif zoneHSM.isVisible = true 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$ = "cecDisplayOn" then m.diagnostics.PrintDebug("Display On") m.CecDisplayOn() else if command$ = "cecDisplayOff" then m.diagnostics.PrintDebug("Display Off") m.CecDisplayOff() else if command$ = "cecSetSourceBrightSign" then m.diagnostics.PrintDebug("CecSetSourceBrightSign") m.CecSetSourceBrightSign() else if command$ = "cecSendString" then cecCommandParameter = parameters["cecAsciiString"] cecCommand$ = cecCommandParameter.GetCurrentParameterValue() cecSubstituteSourceAddressParameter = parameters["cecSubstituteSourceAddress"] if type(cecSubstituteSourceAddressParameter) = "roAssociativeArray" then cecSubstituteSourceAddress$ = cecSubstituteSourceAddressParameter.GetCurrentParameterValue() ' cecSubstituteSourceAddress$ is "true" or "false" else ' old presentation where user did not set this value - for compatibility set this value to true cecSubstituteSourceAddress$ = "true" endif m.diagnostics.PrintDebug("cecSendString: " + cecCommand$ + ", cecSubstituteSourceAddress: " + cecSubstituteSourceAddress$) m.SendCecCommand(cecCommand$, cecSubstituteSourceAddress$) 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) userVariablesChanged = {} userVariablesChanged["EventType"] = "USER_VARIABLES_UPDATED" m.msgPort.PostMessage(userVariablesChanged) ' Notify controlling devices to refresh m.SendUDPNotification("refresh") 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) else if command$ = "beaconStart" then nameParameter = parameters["beaconName"] name$ = nameParameter.GetCurrentParameterValue() m.btManager.StartBeacon(name$) else if command$ = "beaconStop" then nameParameter = parameters["beaconName"] name$ = nameParameter.GetCurrentParameterValue() m.btManager.StopBeacon(name$) 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.WriteVariableLogEntry = WriteVariableLogEntry 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.AddVariableDataToLog = AddVariableDataToLog 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.variableLoggingEnabled = false logging.uploadLogFilesAtBoot = false logging.uploadLogFilesAtSpecificTime = false logging.uploadLogFilesTime% = 0 logging.uploadLogFilesInterval% = invalid 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$+"4" 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 m.AddVariableDataToLog(sourceFilePath$) destinationFilePath$ = "logs/" + file CopyFile(sourceFilePath$, destinationFilePath$) DeleteFile(sourceFilePath$) next End Sub Sub AddVariableDataToLog(filePath$ As String) if m.variableLoggingEnabled then openedDB = false if not m.bsp.variablesDBExists then m.bsp.ReadVariablesDB("") openedDB = true endif if m.bsp.variablesDBExists then logFile = CreateObject("roAppendFile", filePath$) if type(logFile) <> "roAppendFile" return timestamp$ = m.systemTime.GetLocalDateTime().GetString() sectionNames = m.bsp.GetDBSectionNames() for each sectionName in sectionNames m.WriteVariableLogEntry(logFile, timestamp$, "Section", sectionName, "", "") sectionId% = m.bsp.GetDBSectionId(sectionName) if sectionId% > 0 then categoryNames = m.bsp.GetDBCategoryNames(sectionName) for each categoryName in categoryNames m.WriteVariableLogEntry(logFile, timestamp$, "Category", categoryName, "", "") userVariablesList = m.bsp.GetUserVariablesGivenCategory(sectionName, false, categoryName, false) for each userVariable in userVariablesList m.WriteVariableLogEntry(logFile, timestamp$, "Variable", userVariable.name$, userVariable.currentValue$, userVariable.defaultValue$) next next endif next logFile.Flush() if openedDB then m.bsp.userVariablesDB = invalid endif endif endif End Sub Sub WriteVariableLogEntry(logFile As Object, timeStamp$ As String, entityType$ As String, name$ As String, currentValue$ As String, defaultValue$ As String) if not m.variableLoggingEnabled then return if type(logFile) <> "roAppendFile" then return t$ = chr(9) logFile.SendLine("L=u"+t$+"T="+timestamp$+t$+"E="+entityType$+t$+"N="+name$+t$+"C="+currentValue$+t$+"D="+defaultValue$) End Sub Sub InitializeLogging(playbackLoggingEnabled As Boolean, eventLoggingEnabled As Boolean, stateLoggingEnabled As Boolean, diagnosticLoggingEnabled As Boolean, variableLoggingEnabled As Boolean, uploadLogFilesAtBoot As Boolean, uploadLogFilesAtSpecificTime As Boolean, uploadLogFilesTime% As Integer) m.loggingEnabled = playbackLoggingEnabled or eventLoggingEnabled or stateLoggingEnabled or diagnosticLoggingEnabled or variableLoggingEnabled 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.variableLoggingEnabled = variableLoggingEnabled 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, variableLoggingEnabled 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 variableLoggingEnabled = m.variableLoggingEnabled and uploadLogFilesAtBoot = m.uploadLogFilesAtBoot and uploadLogFilesAtSpecificTime = m.uploadLogFilesAtSpecificTime and uploadLogFilesTime% = m.uploadLogFilesTime% then return m.loggingEnabled = playbackLoggingEnabled or eventLoggingEnabled or stateLoggingEnabled or diagnosticLoggingEnabled or variableLoggingEnabled 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.variableLoggingEnabled = variableLoggingEnabled 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 type(m.cutoverTimer) = "roTimer" then m.cutoverTimer.stop() endif m.cutoverTimer = CreateObject("roTimer") m.cutoverTimer.SetPort(m.msgPort) if type(m.uploadLogFilesInterval%) = "Integer" or type(m.uploadLogFilesInterval%) = "roInt" then m.cutoverTimer.SetElapsed(m.uploadLogFilesInterval% * 60, 0) else if m.uploadLogFilesAtSpecificTime then hour% = m.uploadLogFilesTime% / 60 minute% = m.uploadLogFilesTime% - (hour% * 60) else hour% = 0 minute% = 0 endif m.cutoverTimer.SetDate(-1, -1, -1) m.cutoverTimer.SetTime(hour%, minute%, 0) endif 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 30 days old dtExpired = m.systemTime.GetLocalDateTime() dtExpired.SubtractSeconds(60 * 60 * 24 * 30) ' 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 diagnostics.DiagnoseAndRecoverWifiNetwork = DiagnoseAndRecoverWifiNetwork diagnostics.createWifiFailure = false diagnostics.minusSixFailureCount = 0 diagnostics.minusTwentyEightFailureCount = 0 diagnostics.minusFiftySixFailureCount = 0 diagnostics.lastWifiReconnectTimestampInSeconds = 0 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_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_LIVE_MRSS_PLUGIN_FAILURE = "1043" diagnosticCodes.EVENT_EMPTY_MEDIA_PLAYLIST = "1044" diagnosticCodes.EVENT_CUSTOM_USER_AGENT_FAILURE = "1045" 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" diagnosticCodes.EVENT_DELETE_USER_VARIABLES_DB = "1207" diagnosticCodes.EVENT_SCREENSHOT_ERROR = "1208" diagnosticCodes.EVENT_SCREENSHOT_UPLOAD_ERROR = "1209" diagnosticCodes.EVENT_SCREENSHOT_UPLOADED_AND_QUEUED = "1210" diagnosticCodes.EVENT_SCREENSHOT_QUEUE_ERROR = "1211" diagnosticCodes.EVENT_SET_BSN_OVERRIDE = "1212" diagnosticCodes.EVENT_CANCEL_BSN_OVERRIDE = "1213" diagnosticCodes.EVENT_BSN_OVERRIDE_EXPIRED = "1214" diagnosticCodes.EVENT_SET_SNAPSHOT_CONFIGURATION = "1215" diagnosticCodes.EVENT_STREAM_END = "1216" diagnosticCodes.EVENT_SET_VIDEO_MODE = "1217" diagnosticCodes.EVENT_SNAPSHOT_PUT_TO_SERVER_ERROR = "1218" diagnosticCodes.EVENT_CHECK_LIVE_TEXT_FEED_HEAD = "1219" diagnosticCodes.EVENT_SCREENSHOT_UPLOADED = "1220" diagnosticCodes.EVENT_BEACON_START = "1300" diagnosticCodes.EVENT_BEACON_START_FAILED = "1301" diagnosticCodes.EVENT_BEACON_START_LIMIT_EXCEEDED = "1302" diagnosticCodes.EVENT_BTLE_START_FAILED = "1303" diagnosticCodes.EVENT_CONTROL_PORT_DISCONNECTED = "1304" 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 helpers 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.type = "video/quicktime" OR item.type = "video/x-matroska" OR item.medium = "video" or item.type = "audio/mpeg" or item.medium = "audio" or item.type = "text/html" or item.type = "application/widget" or item.medium = "document" 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 Function isHtml( item As Object ) As Boolean REM Default is the item is an image rv = false if item.type = "text/html" or item.type = "application/widget" or item.medium = "document" 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 if not currentSync.ReadFromFile("localSetupToStandalone-sync.xml") then currentSync = invalid endif 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() feedFileName$ = "feed_cache/" + m.name$ + ".xml" m.isMRSSFeed = m.FeedIsMRSS( feedFileName$ ) if not m.isMRSSFeed and m.parser$ = "" 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 ' Do not download content of type 'text/html' - this is accessed directly if item.type = invalid or item.type <> "text/html" then 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 if IsNonEmptyString(item.guid) then asset.change_hint = item.guid else if IsString(item.url) then asset.change_hint = item.url endif m.assetCollection.AddAsset(asset) endif 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$) m.feedContentFilesToDownload = {} for each item in m.feed.items ' Do not download content of type 'text/html' - this is accessed directly if item.type = invalid or item.type <> "text/html" then asset = CreateObject("roAssociativeArray") asset.link = item.url if item.size > 0 then asset.size = item.size endif asset.name = item.url if IsNonEmptyString(item.guid) then asset.change_hint = item.guid else if IsString(item.url) then asset.change_hint = item.url endif m.assetCollection.AddAsset(asset) endif ' track feed content downloads fileToDownload = {} fileToDownload.name = item.title fileToDownload.size = item.size fileToDownload.hash = item.guid fileToDownload.currentFilePercentage$ = "" fileToDownload.status$ = "" if type(asset) = "roAssociativeArray" and type(asset.link) = "roString" then if type(m.assetPoolFiles) = "roAssetPoolFiles" then filePath = m.assetPoolFiles.GetPoolFilePath(asset.link) if filePath <> "" then fileToDownload.currentFilePercentage$ = "100" fileToDownload.status$ = "ok" endif endif endif if IsString(fileToDownload.hash) then m.feedContentFilesToDownload.AddReplace(fileToDownload.hash, fileToDownload) endif next if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.UploadDeviceDownloadProgressFileList() m.bsp.networkingHSM.FileListPendingUpload = false endif 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.bsp.feedPool.ReserveMegabytes(50) m.assetFetcher = CreateObject("roAssetFetcher", m.bsp.feedPool) m.assetFetcher.SetPort(m.bsp.msgPort) m.assetFetcher.AddHeader("User-Agent", m.bsp.userAgent$) 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.ParseFeedByPlugin = mrssFeed_ParseFeedByPlugin feed.SetTTLMinutes = mrssFeed_SetTTLMinutes feed.SetTTLSeconds = mrssFeed_SetTTLSeconds feed.ContentExists = mrssFeed_ContentExists feed.AllContentExists = mrssFeed_AllContentExists return feed End Function Sub ParseMRSSFeed( filePath$ As String ) m.feed = newMRSSFeed( m ) if m.parser$ <> "" then m.feed.ParseFeedByPlugin( filePath$ ) else m.feed.PopulateFeedItems( filePath$ ) endif ' 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 if m.feed.displayOn <> invalid then m.displayOn = m.feed.displayOn 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 Function mrssFeed_AllContentExists( assetPoolFiles As Object ) As Boolean for each item in m.items ' skip checkng for HTML items - bug 24245 if item.type = invalid or item.type <> "text/html" then file$ = item.url filePath$ = GetPoolFilePath(assetPoolFiles, file$) if filePath$ = "" return false endif next return true End Function Sub mrssFeed_PopulateFeedItems( filePath$ As String ) m.items = [] xml = ReadAsciiFile( filePath$ ) if len(xml) = 0 return ' check for encryption if getGlobalAA().bsp.contentEncrypted then isEncrypted = true else isEncrypted = false endif 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 name = "item" then item = newMRSSItem(elt) if (item <> invalid) then item.isEncrypted = isEncrypted 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 Sub mrssFeed_SetTTLSeconds( ttlSeconds As String ) if ttlSeconds <> invalid then secs = Val(ttlSeconds) if secs < 30 then m.ttlSeconds = 30 else m.ttlSeconds = secs endif ' 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 endif End Sub Function newMRSSItem( xml as Object ) As Object item = { durationSeconds: 60, url:"no_url", category:"no_category", thumbnail:"no_thumbnail", title:"no_title", displayStart:0, medium:"no_medium", size:0, isEncrypted: GetGlobalAA().bsp.contentEncrypted } 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 ' custom fields ' ignore the following elements in a feed: link, ? else if name <> "link" then if type(item.mrssCustomFields) <> "roAssociativeArray" then item.mrssCustomFields = {} endif if elt.GetBody() = invalid then value = "" else value = elt.GetBody() endif item.mrssCustomFields.AddReplace(name, value) end if next ' make item.guid the hash of the guid if IsString(item.guid) then hashGen = CreateObject("roHashGenerator", "SHA1") item.guid = hashGen.hash(item.guid).ToHexString() hashGen = invalid endif if (contentPresent) then return item else return invalid end if End Function Sub mrssFeed_ParseFeedByPlugin( filePath$ As String ) items = CreateObject("roArray", 1, true) metadata = CreateObject("roAssociativeArray") m.items = [] ERR_NORMAL_END = &hFC retVal = Eval(m.liveDataFeed.parser$ + "(filePath$, items, metadata)") if retVal <> ERR_NORMAL_END then ' log the failure bsp = m.liveDataFeed.bsp bsp.diagnostics.PrintDebug("Failure invoking Eval to parse live MRSS data feed: return value = " + stri(retVal) + ", parser is " + m.liveDataFeed.parser$) bsp.logging.WriteDiagnosticLogEntry(bsp.diagnosticCodes.EVENT_LIVE_MRSS_PLUGIN_FAILURE, stri(retVal) + chr(9) + m.liveDataFeed.parser$) else ' Use the item array to build the item array for the feed, making sure it has ' all required elements for each item in items ' Skip any item that does not have a url if item.url <> invalid and IsString(item.url) then mrssItem = {} mrssItem.url = item.url if IsString(item.title) then mrssItem.title = item.title else mrssItem.title = "" endif if IsString(item.description) then mrssItem.description = item.description else mrssItem.description = "" endif if IsString(item.description) then mrssItem.duration = Val(item.duration) else mrssItem.duration = 15 endif if IsString(item.type) then mrssItem.type = item.type else mrssItem.type = "" endif if IsString(item.medium) then mrssItem.medium = item.medium else mrssItem.medium = "no_medium" endif if IsString(item.size) then mrssItem.size = Val(item.size) else mrssItem.size = 0 endif if IsString(item.probeData) then mrssItem.probeData = item.probeData else mrssItem.probeData = "" endif ' make mrssItem.guid the hash of the guid from the feed if IsString(item.guid) then guid$ = item.guid hashGen = CreateObject("roHashGenerator", "SHA1") mrssItem.guid = hashGen.hash(guid$).ToHexString() hashGen = invalid endif ' if plugin specified custom fields, just copy them in if type(item.mrssCustomFields) = "roAssociativeArray" then mrssItem.mrssCustomFields = item.mrssCustomFields endif m.items.Push(mrssItem) endif next 'Populate feed metadata 'If the parser gives us an update interval in seconds, use that, otherwise look for interval in minutes (like standard MRSS) if metadata.ttlSeconds <> invalid and IsString(metadata.ttlSeconds) then m.SetTTLSeconds(metadata.ttlSeconds) else if metadata.ttl <> invalid and IsString(metadata.ttl) then m.SetTTLMinutes(metadata.ttl) endif if metadata.title <> invalid and IsString(metadata.title) then m.title = metadata.title endif if metadata.playtime <> invalid and IsString(metadata.playtime) then m.playtime = metadata.playtime endif if metadata.displayOn <> invalid and IsString(metadata.displayOn) then m.displayOn = metadata.displayOn endif endif End Sub 'endregion 'region LiveDataFeed methods Function ParseSimpleRSSFeed(filePath$ As String) As Boolean success = true m.articles = CreateObject("roArray", 1, true) m.articleTitles = CreateObject("roArray", 1, true) m.articlesByTitle = CreateObject("roAssociativeArray") m.articleHashTypes = CreateObject("roArray", 1, true) m.articleHashes = CreateObject("roArray", 1, true) if m.parser$ <> "" then userVariables = m.bsp.currentUserVariables ERR_NORMAL_END = &hFC ' try plugin using new interface first feedItems = [] retVal = Eval(m.parser$ + "(filePath$, feedItems, m.bsp)") if retVal = ERR_NORMAL_END then ' success using updated interface. extract data and convert to structures used by other parts of autorun index% = 0 for each feedItem in feedItems m.articleTitles[index%] = feedItem.key m.articles[index%] = feedItem.url m.articlesByTitle.AddReplace(feedItem.key, feedItem.url) m.articleHashTypes[index%] = feedItem.hashType m.articleHashes[index%] = feedItem.hash index% = index% + 1 next else ' failure using new interface: try old interface retVal = Eval(m.parser$ + "(filePath$, m.articles, m.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 " + m.parser$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_LIVE_TEXT_PLUGIN_FAILURE, stri(retVal) + chr(9) + m.parser$) success = false endif endif else if type(m.isJSON) = "roBoolean" and m.isJSON then jsonString=ReadAsciiFile(filePath$) json = ParseJSON(jsonString) numItemsAdded% = 0 for each jsonItem in json if type(m.isTwitterFeed) = "roBoolean" and m.isTwitterFeed then text$ = jsonItem.full_text else text$ = jsonItem.text endif m.articles.Push(text$) m.articleTitles.Push(text$) m.articlesByTitle.AddReplace(text$, text$) numItemsAdded% = numItemsAdded% + 1 if m.restrictNumberOfItems and (numItemsAdded% >= m.numberOfItemsToDisplay%) then exit for endif next if numItemsAdded% = 0 then success = false endif else parser = CreateObject("roRssParser") success = parser.ParseFile(filePath$) if success then article = parser.GetNextArticle() while type(article) = "roRssArticle" title = article.GetTitle() description = article.GetDescription() m.articles.Push(description) m.articleTitles.Push(title) m.articlesByTitle.AddReplace(title, description) article = parser.GetNextArticle() endwhile endif endif endif return success End Function Function ContentDataFeedsIdentical(articlesFeed1 As Object, articlesFeed2 As Object, compareArticleTitles As Boolean, articleTitlesFeed1 As Object, articleTitlesFeed2 As Object) As Boolean if articlesFeed1.Count() = articlesFeed2.Count() then for i% = 0 to articlesFeed1.Count() - 1 if articlesFeed1[i%] <> articlesFeed2[i%] then return false else if compareArticleTitles and articleTitlesFeed1[i%] <> articleTitlesFeed2[i%] then return false endif next return true endif return false End Function Sub ReadFeedContent() if m.usage$ = "content" then m.ReadLiveFeedContent() else if m.usage$ = "mrss" or m.usage$ = "mrsswith4k" then m.ReadMRSSContent() endif End Sub Sub ReadLiveFeedContent() filePath$ = "feed_cache/" + m.name$ + ".xml" ok=true if m.isDynamicPlaylist or m.isLiveMediaFeed then m.ParseMRSSFeed(filePath$) m.ConvertMRSSFormatToContent() else ok = m.ParseSimpleRSSFeed(filePath$) endif if ok then m.itemUrls = m.articles m.fileUrls = m.articles if type(m.articleMediaTypes) = "roArray" then m.fileTypes = m.articleMediaTypes endif m.fileKeys = CreateObject("roArray", m.articles.Count(), true) if m.articleTitles.Count() > 0 then m.fileKeys = m.articleTitles else if type(m.articlesByTitle) = "roAssociativeArray" then ' the following algorithm has poor performance- improve in the future by building and using a dictionary for each key in m.articlesByTitle ' find the corresponding url by linearly searching through m.articles index% = 0 url = m.articlesByTitle[key] for each articleUrl in m.articles if articleUrl = url then m.fileKeys[index%] = key endif index% = index% + 1 next next endif ' build data structures so that script can check if all content exists on the card m.assetCollection = CreateObject("roAssetCollection") index% = 0 for each url in m.fileUrls asset = CreateObject("roAssociativeArray") asset.link = url asset.name = url if type(m.assets) = "roArray" then asset.hash = m.assets[index%].hash endif m.assetCollection.AddAsset(asset) index% = index% + 1 next ' verify that all specified files are actually on the card m.assetPoolFiles = CreateObject("roAssetPoolFiles", m.bsp.feedPool, m.assetCollection) for each url in m.fileUrls filePath$ = m.assetPoolFiles.GetPoolFilePath(url) if filePath$ = "" then m.assetPoolFiles = invalid m.itemUrls = invalid m.fileKeys = invalid m.fileUrls = invalid return endif next ' protect these 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 ' 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 Sub DownloadLiveFeedContent() m.bsp.diagnostics.PrintDebug("DownloadLiveFeedContent") if type(m.parser$) = "roString" and m.parser$<>"" then compareArticleTitles = false else compareArticleTitles = true endif if type(m.assetFetcher) = "roAssetFetcher" then ' fetch active, see if feed has changed if ContentDataFeedsIdentical(m.articlesDownloading, m.articles, compareArticleTitles, 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.itemUrls, m.articles, compareArticleTitles, m.fileKeys, m.articleTitles) then m.bsp.diagnostics.PrintDebug("No change in content feed " + m.name$ + ". No need to download content.") ' post message indicating no need to download content contentDataFeedUnchanged = CreateObject("roAssociativeArray") contentDataFeedUnchanged["EventType"] = "CONTENT_DATA_FEED_UNCHANGED" contentDataFeedUnchanged["Name"] = m.name$ m.bsp.msgPort.PostMessage(contentDataFeedUnchanged) 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") index% = 0 for each url in m.articles asset = CreateObject("roAssociativeArray") asset.link = url asset.name = url ' Code commented out until fix for bug 17733 / 29442 is fully implemented for usageType = "content" ' asset.change_hint = url if type(m.articleHashTypes) = "roArray" and m.articleHashTypes.Count() > index% and type(m.articleHashTypes[index%]) = "roString" and type(m.articleHashes) = "roArray" and m.articleHashes.Count() > index% and type(m.articleHashes[index%]) = "roString" then asset.hash = m.articleHashTypes[index%] + ":" + m.articleHashes[index%] endif m.assetCollection.AddAsset(asset) ' Code commented out until fix for bug 17733 / 29442 is fully implemented for usageType = "content": the issue is that there is no m.feed / m.feed.items for this type of feed ' ' track feed content downloads ' fileToDownload = {} ' fileToDownload.name = url ' fileToDownload.size = 0 ' fileToDownload.hash = url ' fileToDownload.currentFilePercentage$ = "" ' fileToDownload.status$ = "" ' if type(asset) = "roAssociativeArray" and type(asset.link) = "roString" then ' if type(m.assetPoolFiles) = "roAssetPoolFiles" then ' filePath = m.assetPoolFiles.GetPoolFilePath(asset.link) ' if filePath <> "" then ' fileToDownload.currentFilePercentage$ = "100" ' fileToDownload.status$ = "ok" ' endif ' endif ' endif ' m.feedContentFilesToDownload.AddReplace(fileToDownload.hash, fileToDownload) index% = index% + 1 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.bsp.feedPool.ReserveMegabytes(50) m.assetFetcher = CreateObject("roAssetFetcher", m.bsp.feedPool) m.assetFetcher.SetPort(m.bsp.msgPort) m.assetFetcher.AddHeader("User-Agent", m.bsp.userAgent$) 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() m.bsp.liveDataFeedsByTimer.AddReplace(stri(m.timer.GetIdentity()), m) endif End Sub Sub HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent(event) m.bsp.diagnostics.PrintDebug("### HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent") m.bsp.diagnostics.PrintDebug("### File download progress " + event.GetFileName() + str(event.GetCurrentFilePercentage())) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_FILE_DOWNLOAD_PROGRESS, event.GetFileName() + chr(9) + str(event.GetCurrentFilePercentage())) fileIndex% = event.GetFileIndex() assetList = m.assetCollection.GetAssetList() asset = assetList[fileIndex%] hash = asset.change_hint ' Hash is invalid until fix for bug 17733 / 29442 is fully implemented for usageType = "content": the issue is that there is no m.feed / m.feed.items for this type of feed if type(hash) <> "Invalid" then fileItem = m.feedContentFilesToDownload.Lookup(hash) if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.AddDeviceDownloadProgressItem(fileItem, str(event.GetCurrentFilePercentage()), "ok") endif endif print "----------------------------- HandleLiveDataFeedContentDownloadAssetFetcherProgressEvent: ";event.GetCurrentFilePercentage() 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()) ' log this error to the download progress handler fileIndex% = event.GetFileIndex() assetList = m.assetCollection.GetAssetList() asset = assetList[fileIndex%] if IsString(asset.change_hint) then hash = asset.change_hint fileItem = m.feedContentFilesToDownload.Lookup(hash) if type(fileItem) = "roAssociativeArray" and type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.AddDeviceDownloadProgressItem(fileItem, "-1", event.GetFailureReason()) endif endif ' 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.lastDownloadedFailed = true m.bsp.RemoveFailedFeedFromQueue() 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") ' send up the list of files downloaded m.feedContentFilesToDownload = {} ' m.feed is invalid for feeds used for Media Lists (usageType$ = "content'): progress updates not supported yet if type(m.feed) = "roAssociativeArray" and type(m.feed.items) = "roArray" then for each item in m.feed.items fileToDownload = {} fileToDownload.name = item.title fileToDownload.size = item.size fileToDownload.hash = item.guid fileToDownload.currentFilePercentage$ = "100" fileToDownload.status$ = "ok" if IsString(fileToDownload.hash) then m.feedContentFilesToDownload.AddReplace(fileToDownload.hash, fileToDownload) endif next endif if type(m.bsp.networkingHSM) = "roAssociativeArray" then m.bsp.networkingHSM.UploadDeviceDownloadProgressFileList() m.bsp.networkingHSM.FileListPendingUpload = false endif m.assetFetcher = invalid if m.usage$ = "content" 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.itemUrls = [] m.fileKeys = [] m.fileUrls = [] m.fileTypes = CreateObject("roArray", 1, true) ' for MediaList states titlesByUrl = {} for each title in m.articlesByTitle url = m.articlesByTitle.Lookup(title) titlesByUrl.AddReplace(url, title) next index% = 0 for each itemUrl in m.articles m.itemUrls.push(itemUrl) if type(m.articleMediaTypes) = "roArray" and m.articleMediaTypes.Count() > index% then m.fileTypes[index%] = m.articleMediaTypes[index%] endif ' get corresponding title title = titlesByUrl.Lookup(itemUrl) m.fileKeys.push(title) m.fileUrls.push(itemUrl) index% = index% + 1 next ' post message indicating load complete contentDataFeedLoaded = CreateObject("roAssociativeArray") contentDataFeedLoaded["EventType"] = "CONTENT_DATA_FEED_LOADED" contentDataFeedLoaded["Name"] = m.name$ m.bsp.msgPort.PostMessage(contentDataFeedLoaded) else ' unprotect old assets, keep protection on new (now current) assets 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 if not m.bsp.feedPool.UnprotectAssets("download-" + 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 ' 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 Sub ConvertMRSSFormatToContent() ' convert to format required for content feed m.articles = CreateObject("roArray", 1, true) m.articleTitles = CreateObject("roArray", 1, true) m.articlesByTitle = CreateObject("roAssociativeArray") m.articleMediaTypes = CreateObject("roArray", 1, true) for each item in m.feed.items m.articles.push(item.url) m.articleTitles.push(item.title) m.articlesByTitle.AddReplace(item.title, item.url) m.articleMediaTypes.push(item.medium) next End Sub Function IsFeatureSupported(featureName$ As String, fwVersion$ As String, featureMinRevs As Object) As Boolean featureExists = featureMinRevs.DoesExist(featureName$) if featureExists then featureMinFWRev = featureMinRevs[featureName$] featureMinFWRevVSFWVersion% = CompareFirmwareVersions(featureMinFWRev, fwVersion$) if featureMinFWRevVSFWVersion% <= 0 return true endif return false End Function Function CompareFirmwareVersions(a$ As String, b$ As String) As Integer start_a% = 0 start_b% = 0 while true if start_a% >= len(a$) then if start_b% >= len(b$) then return 0 else return -1 endif else if start_b% >= len(b$) then return 1 endif aChar$ = mid(a$, start_a% + 1, 1) a_digit = IsDigit(aChar$) bChar$ = mid(b$, start_b% + 1, 1) b_digit = IsDigit(bChar$) if a_digit and b_digit then ' Now we need to find the end of each of the sequences of digits. aa = {} aa.index = start_a% a_number% = ReadDigits(a$, aa) start_a% = aa.index bb = {} bb.index = start_b% b_number% = ReadDigits(b$, bb) start_b% = bb.index if a_number% < b_number% then return -1 else if a_number% > b_number% then return 1 endif else if a_digit then ' The first string has a digit but the second one has a ' non-digit so it must be greater. return 1 else if b_digit then return -1 else aChar$ = mid(a$, start_a% + 1, 1) bChar$ = mid(b$, start_b% + 1, 1) if asc(aChar$) < asc(bChar$) then return -1 else if asc(aChar$) > asc(bChar$) return 1 endif ' Otherwise we've dealt with this character start_a% = start_a% + 1 start_b% = start_b% + 1 endif endwhile End Function Function IsDigit(a$ As String) As Boolean if asc(a$) >= 48 and asc(a$) <= 57 return true return false End Function Function ReadDigits(s$ As String, aa As Object) as Integer value% = 0 index% = aa.index sChar$ = mid(s$, index% + 1, 1) while index% < len(s$) and IsDigit(sChar$) new_value% = value% * 10 + asc(sChar$) - asc("0") index% = index% + 1 value% = new_value% if len(s$) > index% then sChar$ = mid(s$, index% + 1, 1) endif endwhile aa.index = index% return value% End Function 'endregion 'region Bluetooth/Beacons Function newBtManager() as Object ' m is BSP btm = { bsp: m, btActive: false, beaconsSupported: false } btm.newBeacon = newBeacon btm.ParsePresentationBeacons = ParsePresentationBeacons btm.ResetPresentationBeacons = ResetPresentationBeacons btm.UpdatePersistentBeacons = UpdatePersistentBeacons btm.AddPersistentBeacon = AddPersistentBeacon btm.StartBeacon = StartBeacon btm.StopBeacon = StopBeacon btm.SetBtAdvertising = SetBtAdvertising btm.StartBtleClient = StartBtleClient btm.StopBtleClient = StopBtleClient btm.SetBtleStatus = SetBtleStatus btm.HandleEvent = btManager_HandleEvent btm.persistentBeacons = { } btm.presentationBeacons = { } btm.btleSupported = false btm.btleClientManager = invalid btm.btleClientServiceData = invalid btm.btleStatus% = 0 if m.beaconsSupported then btm.beaconsSupported = true btm.btManager = CreateObject("roBtManager") btm.btManager.SetPort(m.msgPort) btm.UpdatePersistentBeacons() endif if m.btleSupported then btm.btleSupported = true endif return btm End Function Function btManager_HandleEvent(event as Object) btEventType = event.GetEvent() if btEventType = "add-adapter" then m.bsp.diagnostics.PrintDebug("-- Bluetooth adapter added") m.btActive = true m.SetBtAdvertising() else if btEventType = "remove-adapter" then m.bsp.diagnostics.PrintDebug("-- Bluetooth adapter removed") m.btActive = false end if End Function Function newBeacon(beaconXml as Object, persistent as Boolean) as Object beacon = invalid if m.beaconsSupported then beaconName$ = beaconXml.name.GetText() beaconType$ = beaconXml.type.GetText() beaconData = invalid if beaconType$ = "IBeacon" then beaconId = beaconXml.beaconId.GetText() ' BeaconId must be specified if IsString(beaconId) and Len(beaconId) > 0 then beaconId = LCase(beaconId) major% = Val(beaconXml.data1.GetText()) minor% = Val(beaconXml.data2.GetText()) txp% = Val(beaconXml.txlevel.GetText()) beaconData = { mode: "beacon", beacon_uuid: beaconId, beacon_major: major%, beacon_minor: minor%, beacon_level: txp%, persistent: persistent } endif else if beaconType$ = "EddystoneUrl" then url = beaconXml.beaconId.GetText() if IsString(url) then txp% = Val(beaconXml.txlevel.GetText()) beaconData = { mode: "eddystone-url", url: url, tx_power: txp%, persistent: persistent } endif else if beaconType$ = "EddystoneUid" then txp% = Val(beaconXml.txlevel.GetText()) beaconData = GetEddystoneUidBeaconData(beaconXml.beaconId.GetText(), beaconXml.data1.GetText(), txp%, persistent) endif if beaconData <> invalid then beacon = { name: beaconName$, type: beaconType$, data: beaconData, activate: true, isActive: false } if not persistent and beacon.autostart <> invalid then autostart = beaconXml.autostart.GetText() if not IsString(autostart) or LCase(autostart.Left(1)) <> "t" then beacon.activate = false endif endif endif endif return beacon End Function Function ParsePresentationBeacons(beaconsXml as Object) as Boolean beacons = { } if m.beaconsSupported and type(beaconsXml) = "roXMLList" and beaconsXml.Count() >= 1 then for each beaconXML in beaconsXml beacon = m.newBeacon(beaconXML, false) if beacon <> invalid then beacons.AddReplace(beacon.name, beacon) endif next endif m.presentationBeacons = beacons return not m.presentationBeacons.IsEmpty() End Function Sub ResetPresentationBeacons() if m.beaconsSupported and not m.presentationBeacons.IsEmpty() then m.presentationBeacons = { } m.SetBtAdvertising() endif End Sub Function GetEddystoneUidBeaconData(namespace as String, instance as String, txLevel% as Integer, persistent as Boolean) as Object beaconData = invalid if Len(namespace) > 0 and Len(instance) > 0 then if LCase(namespace.Left(2)) = "0x" then namespace = namespace.mid(2) endif ns = CreateObject("roByteArray") ns.FromHexString(namespace) ' namespace string must have exactly ten bytes pad = 10 - ns.Count() for i=1 to pad ns.Unshift(0) next if LCase(instance.Left(2)) = "0x" then instance = instance.mid(2) endif in = CreateObject("roByteArray") in.FromHexString(instance) ' instance string must have exactly six bytes pad = 6 - in.Count() for i=1 to pad in.Unshift(0) next beaconData = { mode: "eddystone-uid", namespace: ns, instance: in, tx_power: txLevel%, persistent: persistent } endif return beaconData End Function Sub UpdatePersistentBeacons() wasEmpty = m.persistentBeacons.IsEmpty() m.persistentBeacons = { } m.AddPersistentBeacon(m.bsp.registrySettings.beacon1) m.AddPersistentBeacon(m.bsp.registrySettings.beacon2) if not (m.persistentBeacons.IsEmpty() and wasEmpty) then ' Restart beacons to handle any persistent beacon changes m.SetBtAdvertising() endif End Sub Function AddPersistentBeacon(beaconJson as String) as Boolean success = false if IsString(beaconJson) and Len(beaconJson) > 0 then beaconInputData = ParseJson(beaconJson) if type(beaconInputData) = "roAssociativeArray" then beaconName$ = beaconInputData.Name beaconType% = beaconInputData.Type beaconType$ = "" beaconData = invalid if beaconType% = 0 then beaconId = LCase(beaconInputData.BeaconId) if Len(beaconId) > 0 then beaconType$ = "IBeacon" major% = Val(beaconInputData.Data1) minor% = Val(beaconInputData.Data2) txp% = beaconInputData.TxLevel beaconData = { mode: "beacon", beacon_uuid: beaconId, beacon_major: major%, beacon_minor: minor%, beacon_level: txp%, persistent: true } endif else if beaconType% = 1 then url = beaconInputData.BeaconId if Len(url) > 0 then beaconType$ = "EddystoneUrl" txp% = beaconInputData.TxLevel beaconData = { mode: "eddystone-url", url: url, tx_power: txp%, persistent: true } endif else if beaconType% = 2 then beaconType$ = "EddystoneUid" beaconData = GetEddystoneUidBeaconData(beaconInputData.BeaconId, beaconInputData.Data1, beaconInputData.TxLevel, true) endif if beaconData <> invalid then beacon = { name: beaconName$, type: beaconType$, data: beaconData, activate: true, isActive: false } m.persistentBeacons.AddReplace(beacon.name, beacon) endif endif endif return success End Function Function StartBeacon(name as String) as Boolean success = false beacon = m.presentationBeacons[name] if beacon <> invalid then beacon.activate = true success = m.SetBtAdvertising() endif return success End Function Function StopBeacon(name as String) as Boolean success = false beacon = m.presentationBeacons[name] if beacon <> invalid then beacon.activate = false success = m.SetBtAdvertising() endif return success End Function Function StartBtleClient(clientParams as Object, appId as String, txPower as Integer) as Boolean success = false if m.btleSupported then if m.btManager.GetAdapterList().Count() > 0 then m.bsp.diagnostics.PrintDebug("---- Starting BTLE Client manager") m.btleClientManager = CreateObject("roBtClientManager") m.btleClientManager.SetPort(m.bsp.msgPort) success = m.btleClientManager.Start(clientParams) if not success then m.bsp.diagnostics.PrintDebug("-- Start BTLE Client manager failed: "+m.btleClientManager.GetFailureReason()) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BTLE_START_FAILED, "Start BTLE Client manager failed: "+m.btleClientManager.GetFailureReason()) else ' remember service ID m.btleServiceId = clientParams.service_uuid ' set up service data array m.btleClientServiceData = CreateObject("roByteArray") m.btleClientServiceData.FromHexString(appId) if m.btleClientServiceData.Count() < 4 then while m.btleClientServiceData.Count() < 4 m.btleClientServiceData.Unshift(0) end while else if m.btleClientServiceData.Count() > 4 then while m.btleClientServiceData.Count() > 4 m.btleClientServiceData.Pop() end while endif m.btleClientServiceData.push(txPower) m.btleClientServiceData.push(0) ' restart advertising to set connectable flag for all beacons success = m.SetBtAdvertising() endif else m.bsp.diagnostics.PrintDebug("-- Start BTLE Client manager failed - there is no bluetooth adapter") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BTLE_START_FAILED, "Start BTLE Client manager failed - there is no bluetooth adapter") endif endif return success End Function Function StopBtleClient(clientParams as Object) as Boolean success = false if m.btleClientManager then m.bsp.diagnostics.PrintDebug("---- Stopping BTLE Client manager") m.btleClientManager.Stop() m.btleClientManager = invalid m.btleClientServiceData = invalid m.btleServiceId = invalid ' restart advertising to reset connectable flag for all beacons success = m.SetBtAdvertising() endif return success End Function Sub SetBtleStatus(status% as Integer) newStatus% = status% and 255 if newStatus% <> m.btleStatus% then m.btleStatus% = newStatus% m.SetBtAdvertising() endif End Sub Function SetBtAdvertising() success = false if m.beaconsSupported then ' We must have active bluetooth hardware if m.btManager.GetAdapterList().Count() > 0 then ' If a btClientManager is active, we need to set 'connectable' flag in all beacons isConnectable = false sd = invalid if m.btleClientManager <> invalid and m.btleServiceId <> invalid then isConnectable = true sd = {} sd.uuid = m.BtleServiceId sd.data = m.btleClientServiceData sd.data.SetEntry(5, m.btleStatus%) endif ' Get array of beacon data that should be active now beaconDataArray = [ ] for each beaconName in m.persistentBeacons beacon = m.persistentBeacons[beaconName] data = beacon.data data.connectable = isConnectable if sd <> invalid then sdlist = [sd] data.service_data = sdlist end if beaconDataArray.Push(data) next for each beaconName in m.presentationBeacons beacon = m.presentationBeacons[beaconName] if beacon.activate then if beaconDataArray.Count() < 5 then data = beacon.data data.connectable = isConnectable if sd <> invalid then sdlist = [sd] data.service_data = sdlist end if beaconDataArray.Push(data) else ' There is a limit of 5 beacons msg$ = "-- SetBtAdvertising - attempted to set more than 5 beacons - some beacons will not be started" m.bsp.diagnostics.PrintDebug(msg$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BEACON_START_LIMIT_EXCEEDED, msg$) exit for endif endif next ' If we have the BTLE client manager running, and there are no beacons defined, just advertise the client service if beaconDataArray.Count() = 0 and sd <> invalid then sdlist = [sd] data = { mode: "custom", connectable: true, service_data: sdlist } beaconDataArray.Push(data) endif for each beaconName in m.persistentBeacons m.persistentBeacons[beaconName].isActive = false next for each beaconName in m.presentationBeacons m.presentationBeacons[beaconName].isActive = false next if beaconDataArray.Count() > 0 then beaconMsgSpec$ = stri(beaconDataArray.Count())+" beacons" if isConnectable then beaconMsgSpec$ = beaconMsgSpec$ + " (connectable, status =" + stri(m.btleStatus%) + ")" endif success = m.btManager.StartAdvertising(beaconDataArray) if success then m.bsp.diagnostics.PrintDebug("-- Set Bluetooth Advertising for"+beaconMsgSpec$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BEACON_START, "Set Bluetooth Advertising for"+beaconMsgSpec$) for each beaconName in m.persistentBeacons m.persistentBeacons[beaconName].isActive = true next for each beaconName in m.presentationBeacons beacon = m.presentationBeacons[beaconName] if beacon.activate then ' TODO - we shouldn't set active flag for beacons over the limit of 5 beacon.isActive = true endif next else beaconMsgSpec$ = beaconMsgSpec$ + ", reason: "+m.btManager.GetFailureReason() m.bsp.diagnostics.PrintDebug("-- Set Bluetooth Advertising failed for"+beaconMsgSpec$) m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BEACON_START_FAILED, "Set Bluetooth Advertising failed for"+beaconMsgSpec$) endif else success = m.btManager.StopAdvertising() m.bsp.diagnostics.PrintDebug("-- Stop all Bluetooth Advertising") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BEACON_START, "Stop all Bluetooth Advertising") endif else if not (m.persistentBeacons.IsEmpty() and m.presentationBeacons.IsEmpty()) then m.bsp.diagnostics.PrintDebug("-- Set Bluetooth Advertising failed - there is no bluetooth adapter") m.bsp.logging.WriteDiagnosticLogEntry(m.bsp.diagnosticCodes.EVENT_BEACON_START_FAILED, "Set Bluetooth Advertising failed - there is no bluetooth adapter") endif endif return success End Function