/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * RDMHttpModule.cpp * This module acts as the http -> olad gateway for RDM commands. * Copyright (C) 2010 Simon Newton */ #include #include #include #include #include #include #include #include "ola/BaseTypes.h" #include "ola/Callback.h" #include "ola/Logging.h" #include "ola/OlaCallbackClient.h" #include "ola/StringUtils.h" #include "ola/rdm/RDMHelper.h" #include "ola/rdm/UID.h" #include "ola/rdm/UIDSet.h" #include "ola/web/JsonSections.h" #include "olad/OlaServer.h" #include "olad/RDMHttpModule.h" namespace ola { using ola::rdm::UID; using ola::web::BoolItem; using ola::web::HiddenItem; using ola::web::JsonSection; using ola::web::SelectItem; using ola::web::StringItem; using ola::web::UIntItem; using std::endl; using std::pair; using std::string; using std::stringstream; using std::vector; const char RDMHttpModule::BACKEND_DISCONNECTED_ERROR[] = "Failed to send request, client isn't connected"; // global url params const char RDMHttpModule::HINT_KEY[] = "hint"; const char RDMHttpModule::ID_KEY[] = "id"; const char RDMHttpModule::SECTION_KEY[] = "section"; const char RDMHttpModule::UID_KEY[] = "uid"; // url params for particular sections const char RDMHttpModule::ADDRESS_FIELD[] = "address"; const char RDMHttpModule::DISPLAY_INVERT_FIELD[] = "invert"; const char RDMHttpModule::GENERIC_BOOL_FIELD[] = "bool"; const char RDMHttpModule::GENERIC_STRING_FIELD[] = "string"; const char RDMHttpModule::GENERIC_UINT_FIELD[] = "int"; const char RDMHttpModule::IDENTIFY_FIELD[] = "identify"; const char RDMHttpModule::LABEL_FIELD[] = "label"; const char RDMHttpModule::LANGUAGE_FIELD[] = "language"; const char RDMHttpModule::RECORD_SENSOR_FIELD[] = "record"; // section identifiers const char RDMHttpModule::BOOT_SOFTWARE_SECTION[] = "boot_software"; const char RDMHttpModule::CLOCK_SECTION[] = "clock"; const char RDMHttpModule::DEVICE_HOURS_SECTION[] = "device_hours"; const char RDMHttpModule::DEVICE_INFO_SECTION[] = "device_info"; const char RDMHttpModule::DEVICE_LABEL_SECTION[] = "device_label"; const char RDMHttpModule::DISPLAY_INVERT_SECTION[] = "display_invert"; const char RDMHttpModule::DISPLAY_LEVEL_SECTION[] = "display_level"; const char RDMHttpModule::DMX_ADDRESS_SECTION[] = "dmx_address"; const char RDMHttpModule::FACTORY_DEFAULTS_SECTION[] = "factory_defaults"; const char RDMHttpModule::IDENTIFY_SECTION[] = "identify"; const char RDMHttpModule::LAMP_HOURS_SECTION[] = "lamp_hours"; const char RDMHttpModule::LAMP_MODE_SECTION[] = "lamp_mode"; const char RDMHttpModule::LAMP_STATE_SECTION[] = "lamp_state"; const char RDMHttpModule::LAMP_STRIKES_SECITON[] = "lamp_strikes"; const char RDMHttpModule::LANGUAGE_SECTION[] = "language"; const char RDMHttpModule::MANUFACTURER_LABEL_SECTION[] = "manufacturer_label"; const char RDMHttpModule::PAN_INVERT_SECTION[] = "pan_invert"; const char RDMHttpModule::PAN_TILT_SWAP_SECTION[] = "pan_tilt_swap"; const char RDMHttpModule::PERSONALITY_SECTION[] = "personality"; const char RDMHttpModule::POWER_CYCLES_SECTION[] = "power_cycles"; const char RDMHttpModule::POWER_STATE_SECTION[] = "power_state"; const char RDMHttpModule::PRODUCT_DETAIL_SECTION[] = "product_detail"; const char RDMHttpModule::SENSOR_SECTION[] = "sensor"; const char RDMHttpModule::TILT_INVERT_SECTION[] = "tilt_invert"; /** * Create a new OLA HTTP server * @param export_map the ExportMap to display when /debug is called * @param client_socket A ConnectedSocket which is used to communicate with the * server. * @param */ RDMHttpModule::RDMHttpModule(HttpServer *http_server, OlaCallbackClient *client) : HttpModule(http_server, client), m_server(http_server), m_client(client), m_rdm_api(m_client) { m_server->RegisterHandler( "/rdm/run_discovery", NewCallback(this, &RDMHttpModule::RunRDMDiscovery)); m_server->RegisterHandler( "/json/rdm/uids", NewCallback(this, &RDMHttpModule::JsonUIDs)); m_server->RegisterHandler( "/json/rdm/supported_pids", NewCallback(this, &RDMHttpModule::JsonSupportedPIDs)); m_server->RegisterHandler( "/json/rdm/supported_sections", NewCallback(this, &RDMHttpModule::JsonSupportedSections)); m_server->RegisterHandler( "/json/rdm/section_info", NewCallback(this, &RDMHttpModule::JsonSectionInfo)); m_server->RegisterHandler( "/json/rdm/set_section_info", NewCallback(this, &RDMHttpModule::JsonSaveSectionInfo)); } /* * Teardown */ RDMHttpModule::~RDMHttpModule() { map::iterator uid_iter; for (uid_iter = m_universe_uids.begin(); uid_iter != m_universe_uids.end(); uid_iter++) { delete uid_iter->second; } m_universe_uids.clear(); } /** * Run RDM discovery for a universe * @param request the HttpRequest * @param response the HttpResponse * @returns MHD_NO or MHD_YES */ int RDMHttpModule::RunRDMDiscovery(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); bool ok = m_client->ForceDiscovery( universe_id, NewSingleCallback(this, &RDMHttpModule::HandleBoolResponse, response)); if (!ok) return m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR); return MHD_YES; } /** * Return the list of uids for this universe as json * @param request the HttpRequest * @param response the HttpResponse * @returns MHD_NO or MHD_YES */ int RDMHttpModule::JsonUIDs(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); bool ok = m_client->FetchUIDList( universe_id, NewSingleCallback(this, &RDMHttpModule::HandleUIDList, response, universe_id)); if (!ok) return m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR); return MHD_YES; } /** * Return a list of pids supported by this device. This isn't used by the UI * but it's useful for debugging. * @param request the HttpRequest * @param response the HttpResponse * @returns MHD_NO or MHD_YES */ int RDMHttpModule::JsonSupportedPIDs(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); UID *uid = NULL; if (!CheckForInvalidUid(request, &uid)) return m_server->ServeNotFound(response); string error; bool ok = m_rdm_api.GetSupportedParameters( universe_id, *uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::SupportedParamsHandler, response), &error); delete uid; if (!ok) return m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR); return MHD_YES; } /** * Return a list of sections to display in the RDM control panel. * We use the response from SUPPORTED_PARAMS and DEVICE_INFO to decide which * pids exist. * @param request the HttpRequest * @param response the HttpResponse * @returns MHD_NO or MHD_YES */ int RDMHttpModule::JsonSupportedSections(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); UID *uid = NULL; if (!CheckForInvalidUid(request, &uid)) return m_server->ServeNotFound(response); string error; bool ok = m_rdm_api.GetSupportedParameters( universe_id, *uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::SupportedSectionsHandler, response, universe_id, *uid), &error); delete uid; if (!ok) return m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR); return MHD_YES; } /** * Get the information required to render a section in the RDM controller panel */ int RDMHttpModule::JsonSectionInfo(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); UID *uid = NULL; if (!CheckForInvalidUid(request, &uid)) return m_server->ServeNotFound(response); string section_id = request->GetParameter(SECTION_KEY); string error; if (section_id == DEVICE_INFO_SECTION) { error = GetDeviceInfo(request, response, universe_id, *uid); } else if (section_id == PRODUCT_DETAIL_SECTION) { error = GetProductIds(request, response, universe_id, *uid); } else if (section_id == MANUFACTURER_LABEL_SECTION) { error = GetManufacturerLabel(request, response, universe_id, *uid); } else if (section_id == DEVICE_LABEL_SECTION) { error = GetDeviceLabel(request, response, universe_id, *uid); } else if (section_id == FACTORY_DEFAULTS_SECTION) { error = GetFactoryDefaults(response, universe_id, *uid); } else if (section_id == LANGUAGE_SECTION) { error = GetLanguage(response, universe_id, *uid); } else if (section_id == BOOT_SOFTWARE_SECTION) { error = GetBootSoftware(response, universe_id, *uid); } else if (section_id == PERSONALITY_SECTION) { error = GetPersonalities(request, response, universe_id, *uid); } else if (section_id == DMX_ADDRESS_SECTION) { error = GetStartAddress(request, response, universe_id, *uid); } else if (section_id == SENSOR_SECTION) { error = GetSensor(request, response, universe_id, *uid); } else if (section_id == DEVICE_HOURS_SECTION) { error = GetDeviceHours(request, response, universe_id, *uid); } else if (section_id == LAMP_HOURS_SECTION) { error = GetLampHours(request, response, universe_id, *uid); } else if (section_id == LAMP_MODE_SECTION) { error = GetLampMode(request, response, universe_id, *uid); } else if (section_id == LAMP_STATE_SECTION) { error = GetLampState(request, response, universe_id, *uid); } else if (section_id == LAMP_STRIKES_SECITON) { error = GetLampStrikes(request, response, universe_id, *uid); } else if (section_id == POWER_CYCLES_SECTION) { error = GetPowerCycles(request, response, universe_id, *uid); } else if (section_id == DISPLAY_INVERT_SECTION) { error = GetDisplayInvert(response, universe_id, *uid); } else if (section_id == DISPLAY_LEVEL_SECTION) { error = GetDisplayLevel(response, universe_id, *uid); } else if (section_id == PAN_INVERT_SECTION) { error = GetPanInvert(response, universe_id, *uid); } else if (section_id == TILT_INVERT_SECTION) { error = GetTiltInvert(response, universe_id, *uid); } else if (section_id == PAN_TILT_SWAP_SECTION) { error = GetPanTiltSwap(response, universe_id, *uid); } else if (section_id == CLOCK_SECTION) { error = GetClock(response, universe_id, *uid); } else if (section_id == IDENTIFY_SECTION) { error = GetIdentifyMode(response, universe_id, *uid); } else if (section_id == POWER_STATE_SECTION) { error = GetPowerState(response, universe_id, *uid); } else { OLA_INFO << "Missing or unknown section id: " << section_id; return m_server->ServeNotFound(response); } if (!error.empty()) return m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); return MHD_YES; } /** * Save the information for a section or item. */ int RDMHttpModule::JsonSaveSectionInfo(const HttpRequest *request, HttpResponse *response) { unsigned int universe_id; if (!CheckForInvalidId(request, &universe_id)) return m_server->ServeNotFound(response); UID *uid = NULL; if (!CheckForInvalidUid(request, &uid)) return m_server->ServeNotFound(response); string section_id = request->GetParameter(SECTION_KEY); string error; if (section_id == DEVICE_LABEL_SECTION) { error = SetDeviceLabel(request, response, universe_id, *uid); } else if (section_id == FACTORY_DEFAULTS_SECTION) { error = SetFactoryDefault(response, universe_id, *uid); } else if (section_id == LANGUAGE_SECTION) { error = SetLanguage(request, response, universe_id, *uid); } else if (section_id == PERSONALITY_SECTION) { error = SetPersonality(request, response, universe_id, *uid); } else if (section_id == DMX_ADDRESS_SECTION) { error = SetStartAddress(request, response, universe_id, *uid); } else if (section_id == SENSOR_SECTION) { error = RecordSensor(request, response, universe_id, *uid); } else if (section_id == DEVICE_HOURS_SECTION) { error = SetDeviceHours(request, response, universe_id, *uid); } else if (section_id == LAMP_HOURS_SECTION) { error = SetLampHours(request, response, universe_id, *uid); } else if (section_id == LAMP_MODE_SECTION) { error = SetLampMode(request, response, universe_id, *uid); } else if (section_id == LAMP_STATE_SECTION) { error = SetLampState(request, response, universe_id, *uid); } else if (section_id == LAMP_STRIKES_SECITON) { error = SetLampStrikes(request, response, universe_id, *uid); } else if (section_id == POWER_CYCLES_SECTION) { error = SetPowerCycles(request, response, universe_id, *uid); } else if (section_id == DISPLAY_INVERT_SECTION) { error = SetDisplayInvert(request, response, universe_id, *uid); } else if (section_id == DISPLAY_LEVEL_SECTION) { error = SetDisplayLevel(request, response, universe_id, *uid); } else if (section_id == PAN_INVERT_SECTION) { error = SetPanInvert(request, response, universe_id, *uid); } else if (section_id == TILT_INVERT_SECTION) { error = SetTiltInvert(request, response, universe_id, *uid); } else if (section_id == PAN_TILT_SWAP_SECTION) { error = SetPanTiltSwap(request, response, universe_id, *uid); } else if (section_id == IDENTIFY_SECTION) { error = SetIdentifyMode(request, response, universe_id, *uid); } else if (section_id == POWER_STATE_SECTION) { error = SetPowerState(request, response, universe_id, *uid); } else { OLA_INFO << "Missing or unknown section id: " << section_id; return m_server->ServeNotFound(response); } if (!error.empty()) return RespondWithError(response, error); return MHD_YES; } /** * This is called from the main http server whenever a new list of active * universes is received. It's used to prune the uid map so we don't bother * trying to resolve uids for universes that no longer exist. */ void RDMHttpModule::PruneUniverseList(const vector &universes) { map::iterator uid_iter; for (uid_iter = m_universe_uids.begin(); uid_iter != m_universe_uids.end(); uid_iter++) { uid_iter->second->active = false; } vector::const_iterator iter; for (iter = universes.begin(); iter != universes.end(); ++iter) { uid_iter = m_universe_uids.find(iter->Id()); if (uid_iter != m_universe_uids.end()) uid_iter->second->active = true; } // clean up the uid map for those universes that no longer exist for (uid_iter = m_universe_uids.begin(); uid_iter != m_universe_uids.end();) { if (!uid_iter->second->active) { OLA_DEBUG << "removing " << uid_iter->first << " from the uid map"; delete uid_iter->second; m_universe_uids.erase(uid_iter++); } else { uid_iter++; } } } /* * Handle the UID list response. * @param response the HttpResponse that is associated with the request. * @param uids the UIDs for this response. * @param error an error string. */ void RDMHttpModule::HandleUIDList(HttpResponse *response, unsigned int universe_id, const ola::rdm::UIDSet &uids, const string &error) { if (!error.empty()) { m_server->ServeError(response, error); return; } ola::rdm::UIDSet::Iterator iter = uids.Begin(); uid_resolution_state *uid_state = GetUniverseUidsOrCreate(universe_id); // mark all uids as inactive so we can remove the unused ones at the end map::iterator uid_iter; for (uid_iter = uid_state->resolved_uids.begin(); uid_iter != uid_state->resolved_uids.end(); ++uid_iter) uid_iter->second.active = false; stringstream str; str << "{" << endl; str << " \"universe\": " << universe_id << "," << endl; str << " \"uids\": [" << endl; for (; iter != uids.End(); ++iter) { uid_iter = uid_state->resolved_uids.find(*iter); string manufacturer = ""; string device = ""; if (uid_iter == uid_state->resolved_uids.end()) { // schedule resolution uid_state->pending_uids.push( std::pair(*iter, RESOLVE_MANUFACTURER)); uid_state->pending_uids.push( std::pair(*iter, RESOLVE_DEVICE)); resolved_uid uid_descriptor = {"", "", true}; uid_state->resolved_uids[*iter] = uid_descriptor; OLA_DEBUG << "Adding UID " << uid_iter->first << " to resolution queue"; } else { manufacturer = uid_iter->second.manufacturer; device = uid_iter->second.device; uid_iter->second.active = true; } str << " {" << endl; str << " \"manufacturer_id\": " << iter->ManufacturerId() << "," << endl; str << " \"device_id\": " << iter->DeviceId() << "," << endl; str << " \"device\": \"" << EscapeString(device) << "\"," << endl; str << " \"manufacturer\": \"" << EscapeString(manufacturer) << "\"," << endl; str << " }," << endl; } str << " ]" << endl; str << "}"; response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append(str.str()); response->Send(); delete response; // remove any old uids for (uid_iter = uid_state->resolved_uids.begin(); uid_iter != uid_state->resolved_uids.end();) { if (!uid_iter->second.active) { OLA_DEBUG << "Removed UID " << uid_iter->first; uid_state->resolved_uids.erase(uid_iter++); } else { ++uid_iter; } } if (!uid_state->uid_resolution_running) ResolveNextUID(universe_id); } /* * Send the RDM command needed to resolve the next uid in the queue * @param universe_id the universe id to resolve the next UID for. */ void RDMHttpModule::ResolveNextUID(unsigned int universe_id) { bool sent_request = false; string error; uid_resolution_state *uid_state = GetUniverseUids(universe_id); if (!uid_state) return; while (!sent_request) { if (!uid_state->pending_uids.size()) { uid_state->uid_resolution_running = false; return; } uid_state->uid_resolution_running = true; pair &uid_action_pair = uid_state->pending_uids.front(); if (uid_action_pair.second == RESOLVE_MANUFACTURER) { OLA_DEBUG << "sending manufacturer request for " << uid_action_pair.first; sent_request = m_rdm_api.GetManufacturerLabel( universe_id, uid_action_pair.first, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::UpdateUIDManufacturerLabel, universe_id, uid_action_pair.first), &error); OLA_DEBUG << "return code was " << sent_request; uid_state->pending_uids.pop(); } else if (uid_action_pair.second == RESOLVE_DEVICE) { OLA_INFO << "sending device request for " << uid_action_pair.first; sent_request = m_rdm_api.GetDeviceLabel( universe_id, uid_action_pair.first, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::UpdateUIDDeviceLabel, universe_id, uid_action_pair.first), &error); uid_state->pending_uids.pop(); OLA_DEBUG << "return code was " << sent_request; } else { OLA_WARN << "Unknown UID resolve action " << static_cast(uid_action_pair.second); } } } /* * Handle the manufacturer label response. */ void RDMHttpModule::UpdateUIDManufacturerLabel( unsigned int universe, UID uid, const ola::rdm::ResponseStatus &status, const string &manufacturer_label) { uid_resolution_state *uid_state = GetUniverseUids(universe); if (!uid_state) return; if (CheckForRDMSuccess(status)) { map::iterator uid_iter; uid_iter = uid_state->resolved_uids.find(uid); if (uid_iter != uid_state->resolved_uids.end()) uid_iter->second.manufacturer = manufacturer_label; } ResolveNextUID(universe); } /* * Handle the device label response. */ void RDMHttpModule::UpdateUIDDeviceLabel( unsigned int universe, UID uid, const ola::rdm::ResponseStatus &status, const string &device_label) { uid_resolution_state *uid_state = GetUniverseUids(universe); if (!uid_state) return; if (CheckForRDMSuccess(status)) { map::iterator uid_iter; uid_iter = uid_state->resolved_uids.find(uid); if (uid_iter != uid_state->resolved_uids.end()) uid_iter->second.device = device_label; } ResolveNextUID(universe); } /* * Get the UID resolution state for a particular universe * @param universe the id of the universe to get the state for */ RDMHttpModule::uid_resolution_state *RDMHttpModule::GetUniverseUids( unsigned int universe) { map::iterator iter = m_universe_uids.find(universe); return iter == m_universe_uids.end() ? NULL : iter->second; } /* * Get the UID resolution state for a particular universe or create one if it * doesn't exist. * @param universe the id of the universe to get the state for */ RDMHttpModule::uid_resolution_state *RDMHttpModule::GetUniverseUidsOrCreate( unsigned int universe) { map::iterator iter = m_universe_uids.find(universe); if (iter == m_universe_uids.end()) { OLA_DEBUG << "Adding a new state entry for " << universe; uid_resolution_state *state = new uid_resolution_state(); state->uid_resolution_running = false; state->active = true; pair p(universe, state); iter = m_universe_uids.insert(p).first; } return iter->second; } /* * Handle the response from a supported params request */ void RDMHttpModule::SupportedParamsHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status, const vector &pids) { stringstream str; if (CheckForRDMSuccess(status)) { vector::const_iterator iter = pids.begin(); str << "{" << endl; str << " \"pids\": [" << endl; for (; iter != pids.end(); ++iter) { str << " 0x" << std::hex << *iter << ",\n"; } str << " ]" << endl; str << "}"; } response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append(str.str()); response->Send(); delete response; } /** * Takes the supported pids for a device and come up with the list of sections * to display in the RDM panel */ void RDMHttpModule::SupportedSectionsHandler( HttpResponse *response, unsigned int universe_id, UID uid, const ola::rdm::ResponseStatus &status, const vector &pid_list) { string error; // nacks here are ok if the device doesn't support SUPPORTED_PARAMS if (!CheckForRDMSuccess(status) && status.ResponseType() != ola::rdm::ResponseStatus::REQUEST_NACKED) { m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); return; } m_rdm_api.GetDeviceInfo( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::SupportedSectionsDeviceInfoHandler, response, pid_list), &error); if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); } /** * Handle the second part of the supported sections request. */ void RDMHttpModule::SupportedSectionsDeviceInfoHandler( HttpResponse *response, const vector pid_list, const ola::rdm::ResponseStatus &status, const ola::rdm::DeviceDescriptor &device) { vector sections; std::set pids; copy(pid_list.begin(), pid_list.end(), inserter(pids, pids.end())); // PID_DEVICE_INFO is required so we always add it string hint; if (pids.find(ola::rdm::PID_DEVICE_MODEL_DESCRIPTION) != pids.end()) hint.push_back('m'); // m is for device model AddSection(§ions, DEVICE_INFO_SECTION, "Device Info", hint); AddSection(§ions, IDENTIFY_SECTION, "Identify Mode", hint); bool dmx_address_added = false; bool include_software_version = false; vector::const_iterator iter = pid_list.begin(); for (; iter != pid_list.end(); ++iter) { switch (*iter) { case ola::rdm::PID_PRODUCT_DETAIL_ID_LIST: AddSection(§ions, PRODUCT_DETAIL_SECTION, "Product Details"); break; case ola::rdm::PID_MANUFACTURER_LABEL: AddSection(§ions, MANUFACTURER_LABEL_SECTION, "Manufacturer Label"); break; case ola::rdm::PID_DEVICE_LABEL: AddSection(§ions, DEVICE_LABEL_SECTION, "Device Label"); break; case ola::rdm::PID_FACTORY_DEFAULTS: AddSection(§ions, FACTORY_DEFAULTS_SECTION, "Factory Defaults"); break; case ola::rdm::PID_LANGUAGE: AddSection(§ions, LANGUAGE_SECTION, "Language"); break; case ola::rdm::PID_BOOT_SOFTWARE_VERSION_ID: case ola::rdm::PID_BOOT_SOFTWARE_VERSION_LABEL: include_software_version = true; break; case ola::rdm::PID_DMX_PERSONALITY: if (pids.find(ola::rdm::PID_DMX_PERSONALITY_DESCRIPTION) == pids.end()) AddSection(§ions, PERSONALITY_SECTION, "DMX Personality"); else AddSection(§ions, PERSONALITY_SECTION, "DMX Personality", "l"); break; case ola::rdm::PID_DMX_START_ADDRESS: AddSection(§ions, DMX_ADDRESS_SECTION, "DMX Start Address"); dmx_address_added = true; break; case ola::rdm::PID_DEVICE_HOURS: AddSection(§ions, DEVICE_HOURS_SECTION, "Device Hours"); break; case ola::rdm::PID_LAMP_HOURS: AddSection(§ions, LAMP_HOURS_SECTION, "Lamp Hours"); break; case ola::rdm::PID_LAMP_STRIKES: AddSection(§ions, LAMP_STRIKES_SECITON, "Lamp Strikes"); break; case ola::rdm::PID_LAMP_STATE: AddSection(§ions, LAMP_STATE_SECTION, "Lamp State"); break; case ola::rdm::PID_LAMP_ON_MODE: AddSection(§ions, LAMP_MODE_SECTION, "Lamp On Mode"); break; case ola::rdm::PID_DEVICE_POWER_CYCLES: AddSection(§ions, POWER_CYCLES_SECTION, "Device Power Cycles"); break; case ola::rdm::PID_DISPLAY_INVERT: AddSection(§ions, DISPLAY_INVERT_SECTION, "Display Invert"); break; case ola::rdm::PID_DISPLAY_LEVEL: AddSection(§ions, DISPLAY_LEVEL_SECTION, "Display Level"); break; case ola::rdm::PID_PAN_INVERT: AddSection(§ions, PAN_INVERT_SECTION, "Pan Invert"); break; case ola::rdm::PID_TILT_INVERT: AddSection(§ions, TILT_INVERT_SECTION, "Tilt Invert"); break; case ola::rdm::PID_PAN_TILT_SWAP: AddSection(§ions, PAN_TILT_SWAP_SECTION, "Pan/Tilt Swap"); break; case ola::rdm::PID_REAL_TIME_CLOCK: AddSection(§ions, CLOCK_SECTION, "Clock"); break; case ola::rdm::PID_POWER_STATE: AddSection(§ions, POWER_STATE_SECTION, "Power State"); break; } } if (include_software_version) AddSection(§ions, BOOT_SOFTWARE_SECTION, "Boot Software Version"); if (CheckForRDMSuccess(status)) { if (device.dmx_footprint && !dmx_address_added) AddSection(§ions, DMX_ADDRESS_SECTION, "DMX Start Address"); if (device.sensor_count && pids.find(ola::rdm::PID_SENSOR_DEFINITION) != pids.end() && pids.find(ola::rdm::PID_SENSOR_VALUE) != pids.end()) { // sensors count from 1 for (unsigned int i = 0; i < device.sensor_count; ++i) { stringstream heading, hint; hint << i; heading << "Sensor " << (i + 1); AddSection(§ions, SENSOR_SECTION, heading.str(), hint.str()); } } } sort(sections.begin(), sections.end(), lt_section_info()); vector::const_iterator section_iter; stringstream str; str << "[" << endl; for (section_iter = sections.begin(); section_iter != sections.end(); ++section_iter) { str << " {" << endl; str << " \"id\": \"" << section_iter->id << "\"," << endl; str << " \"name\": \"" << section_iter->name << "\"," << endl; str << " \"hint\": \"" << section_iter->hint << "\"," << endl; str << " }," << endl; } str << "]" << endl; response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append(str.str()); response->Send(); delete response; } /* * Handle the request for the device info section. */ string RDMHttpModule::GetDeviceInfo(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string hint = request->GetParameter(HINT_KEY); string error; device_info dev_info = {universe_id, uid, hint, "", ""}; m_rdm_api.GetSoftwareVersionLabel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetSoftwareVersionHandler, response, dev_info), &error); return error; } /** * Handle the response to a software version call. */ void RDMHttpModule::GetSoftwareVersionHandler( HttpResponse *response, device_info dev_info, const ola::rdm::ResponseStatus &status, const string &software_version) { string error; if (CheckForRDMSuccess(status)) dev_info.software_version = software_version; if (dev_info.hint.find('m') != string::npos) { m_rdm_api.GetDeviceModelDescription( dev_info.universe_id, dev_info.uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetDeviceModelHandler, response, dev_info), &error); } else { m_rdm_api.GetDeviceInfo( dev_info.universe_id, dev_info.uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetDeviceInfoHandler, response, dev_info), &error); } if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); } /** * Handle the response to a device model call. */ void RDMHttpModule::GetDeviceModelHandler( HttpResponse *response, device_info dev_info, const ola::rdm::ResponseStatus &status, const string &device_model) { string error; if (CheckForRDMSuccess(status)) dev_info.device_model = device_model; m_rdm_api.GetDeviceInfo( dev_info.universe_id, dev_info.uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetDeviceInfoHandler, response, dev_info), &error); if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); } /** * Handle the response to a device info call and build the response */ void RDMHttpModule::GetDeviceInfoHandler( HttpResponse *response, device_info dev_info, const ola::rdm::ResponseStatus &status, const ola::rdm::DeviceDescriptor &device) { JsonSection section; if (CheckForRDMError(response, status)) return; stringstream stream; stream << static_cast(device.protocol_version_high) << "." << static_cast(device.protocol_version_low); section.AddItem(new StringItem("Protocol Version", stream.str())); stream.str(""); if (dev_info.device_model.empty()) stream << device.device_model; else stream << dev_info.device_model << " (" << device.device_model << ")"; section.AddItem(new StringItem("Device Model", stream.str())); section.AddItem(new StringItem( "Product Category", ola::rdm::ProductCategoryToString(device.product_category))); stream.str(""); if (dev_info.software_version.empty()) stream << device.software_version; else stream << dev_info.software_version << " (" << device.software_version << ")"; section.AddItem(new StringItem("Software Version", stream.str())); section.AddItem(new UIntItem("DMX Footprint", device.dmx_footprint)); stream.str(""); stream << static_cast(device.current_personality) << " of " << static_cast(device.personaility_count); section.AddItem(new StringItem("Personality", stream.str())); section.AddItem(new UIntItem("Sub Devices", device.sub_device_count)); section.AddItem(new UIntItem("Sensors", device.sensor_count)); RespondWithSection(response, section); } /* * Handle the request for the product details ids. */ string RDMHttpModule::GetProductIds(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetProductDetailIdList( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetProductIdsHandler, response), &error); return error; (void) request; } /** * Handle the response to a product detail ids call and build the response. */ void RDMHttpModule::GetProductIdsHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status, const vector &ids) { if (CheckForRDMError(response, status)) return; bool first = true; stringstream product_ids; JsonSection section; vector::const_iterator iter = ids.begin(); for (; iter != ids.end(); ++iter) { string product_id = ola::rdm::ProductDetailToString(*iter); if (product_id.empty()) continue; if (first) first = false; else product_ids << ", "; product_ids << product_id; } section.AddItem(new StringItem("Product Detail IDs", product_ids.str())); RespondWithSection(response, section); } /** * Handle the request for the Manufacturer label. */ string RDMHttpModule::GetManufacturerLabel(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetManufacturerLabel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetManufacturerLabelHandler, response, universe_id, uid), &error); return error; (void) request; } /** * Handle the response to a manufacturer label call and build the response */ void RDMHttpModule::GetManufacturerLabelHandler( HttpResponse *response, unsigned int universe_id, const UID uid, const ola::rdm::ResponseStatus &status, const string &label) { if (CheckForRDMError(response, status)) return; JsonSection section; section.AddItem(new StringItem("Manufacturer Label", label)); RespondWithSection(response, section); // update the map as well uid_resolution_state *uid_state = GetUniverseUids(universe_id); if (uid_state) { map::iterator uid_iter = uid_state->resolved_uids.find(uid); if (uid_iter != uid_state->resolved_uids.end()) uid_iter->second.manufacturer = label; } } /** * Handle the request for the Device label. */ string RDMHttpModule::GetDeviceLabel(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDeviceLabel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetDeviceLabelHandler, response, universe_id, uid), &error); return error; (void) request; } /** * Handle the response to a device label call and build the response */ void RDMHttpModule::GetDeviceLabelHandler( HttpResponse *response, unsigned int universe_id, const UID uid, const ola::rdm::ResponseStatus &status, const string &label) { if (CheckForRDMError(response, status)) return; JsonSection section; section.AddItem(new StringItem("Device Label", label, LABEL_FIELD)); RespondWithSection(response, section); // update the map as well uid_resolution_state *uid_state = GetUniverseUids(universe_id); if (uid_state) { map::iterator uid_iter = uid_state->resolved_uids.find(uid); if (uid_iter != uid_state->resolved_uids.end()) uid_iter->second.device = label; } } /* * Set the device label */ string RDMHttpModule::SetDeviceLabel(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string label = request->GetParameter(LABEL_FIELD); string error; m_rdm_api.SetDeviceLabel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, label, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the factory defaults section */ string RDMHttpModule::GetFactoryDefaults(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetFactoryDefaults( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::FactoryDefaultsHandler, response), &error); return error; } /** * Handle the response to a factory defaults call and build the response */ void RDMHttpModule::FactoryDefaultsHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status, bool defaults) { if (CheckForRDMError(response, status)) return; JsonSection section; section.AddItem(new StringItem("Using Defaults", defaults ? "Yes" : "No")); section.AddItem(new HiddenItem("1", GENERIC_UINT_FIELD)); section.SetSaveButton("Reset to Defaults"); RespondWithSection(response, section); } /* * Reset to the factory defaults */ string RDMHttpModule::SetFactoryDefault(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.ResetToFactoryDefaults( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the language section. */ string RDMHttpModule::GetLanguage(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetLanguageCapabilities( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetSupportedLanguagesHandler, response, universe_id, uid), &error); return error; } /** * Handle the response to language capability call. */ void RDMHttpModule::GetSupportedLanguagesHandler( HttpResponse *response, unsigned int universe_id, const UID uid, const ola::rdm::ResponseStatus &status, const vector &languages) { string error; m_rdm_api.GetLanguage( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetLanguageHandler, response, languages), &error); if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); (void) status; } /** * Handle the response to language call and build the response */ void RDMHttpModule::GetLanguageHandler(HttpResponse *response, vector languages, const ola::rdm::ResponseStatus &status, const string &language) { JsonSection section; SelectItem *item = new SelectItem("Language", LANGUAGE_FIELD); bool ok = CheckForRDMSuccess(status); vector::const_iterator iter = languages.begin(); unsigned int i = 0; for (; iter != languages.end(); ++iter, i++) { item->AddItem(*iter, *iter); if (ok && *iter == language) item->SetSelectedOffset(i); } if (ok && !languages.size()) { item->AddItem(language, language); item->SetSelectedOffset(0); } section.AddItem(item); RespondWithSection(response, section); } /* * Set the language */ string RDMHttpModule::SetLanguage(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string label = request->GetParameter(LANGUAGE_FIELD); string error; m_rdm_api.SetLanguage( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, label, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the boot software section. */ string RDMHttpModule::GetBootSoftware(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetBootSoftwareVersionLabel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetBootSoftwareLabelHandler, response, universe_id, uid), &error); return error; } /** * Handle the response to a boot software label. */ void RDMHttpModule::GetBootSoftwareLabelHandler( HttpResponse *response, unsigned int universe_id, const UID uid, const ola::rdm::ResponseStatus &status, const string &label) { string error; m_rdm_api.GetBootSoftwareVersion( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetBootSoftwareVersionHandler, response, label), &error); if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); (void) status; } /** * Handle the response to a boot software version. */ void RDMHttpModule::GetBootSoftwareVersionHandler( HttpResponse *response, string label, const ola::rdm::ResponseStatus &status, uint32_t version) { stringstream str; str << label; if (CheckForRDMSuccess(status)) { if (!label.empty()) str << " (" << version << ")"; else str << version; } JsonSection section; StringItem *item = new StringItem("Boot Software", str.str()); section.AddItem(item); RespondWithSection(response, section); } /** * Handle the request for the personality section. */ string RDMHttpModule::GetPersonalities(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string hint = request->GetParameter(HINT_KEY); string error; personality_info *info = new personality_info; info->universe_id = universe_id; info->uid = new UID(uid); info->include_descriptions = (hint == "l"); info->active = 0; info->next = 1; info->total = 0; m_rdm_api.GetDMXPersonality( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetPersonalityHandler, response, info), &error); return error; (void) request; } /** * Handle the response to a dmx personality call. */ void RDMHttpModule::GetPersonalityHandler( HttpResponse *response, personality_info *info, const ola::rdm::ResponseStatus &status, uint8_t current, uint8_t total) { if (CheckForRDMError(response, status)) { delete info->uid; delete info; return; } info->active = current; info->total = total; if (info->include_descriptions) GetNextPersonalityDescription(response, info); else SendPersonalityResponse(response, info); } /** * Get the description of the next dmx personality */ void RDMHttpModule::GetNextPersonalityDescription(HttpResponse *response, personality_info *info) { string error; while (info->next <= info->total) { bool r = m_rdm_api.GetDMXPersonalityDescription( info->universe_id, *(info->uid), ola::rdm::ROOT_RDM_DEVICE, info->next, NewSingleCallback(this, &RDMHttpModule::GetPersonalityLabelHandler, response, info), &error); if (r) return; info->next++; } SendPersonalityResponse(response, info); } /** * Handle the response to a Personality label call. This fetches the next * personality in the sequence, or sends the response if we have all the info. */ void RDMHttpModule::GetPersonalityLabelHandler( HttpResponse *response, personality_info *info, const ola::rdm::ResponseStatus &status, uint8_t personality, uint16_t slot_count, const string &label) { string description = ""; uint32_t slots = INVALID_PERSONALITY; if (CheckForRDMSuccess(status)) { slots = slot_count; description = label; } info->personalities.push_back(pair(slots, description)); if (info->next == info->total) { SendPersonalityResponse(response, info); } else { info->next++; GetNextPersonalityDescription(response, info); } (void) personality; } /** * Send the response to a dmx personality section */ void RDMHttpModule::SendPersonalityResponse(HttpResponse *response, personality_info *info) { JsonSection section; SelectItem *item = new SelectItem("Personality", GENERIC_UINT_FIELD); for (unsigned int i = 1; i <= info->total; i++) { if (i <= info->personalities.size() && info->personalities[i - 1].first != INVALID_PERSONALITY) { stringstream str; str << info->personalities[i - 1].second << " (" << info->personalities[i - 1].first << ")"; item->AddItem(str.str(), i); } else { item->AddItem(IntToString(i), i); } if (info->active == i) item->SetSelectedOffset(i - 1); } section.AddItem(item); RespondWithSection(response, section); delete info->uid; delete info; } /** * Set the personality */ string RDMHttpModule::SetPersonality(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string personality_str = request->GetParameter(GENERIC_UINT_FIELD); uint8_t personality; if (!StringToUInt8(personality_str, &personality)) { return "Invalid personality"; } string error; m_rdm_api.SetDMXPersonality( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, personality, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the start address section. */ string RDMHttpModule::GetStartAddress(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDMXAddress( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GetStartAddressHandler, response), &error); return error; (void) request; } /** * Handle the response to a dmx start address call and build the response */ void RDMHttpModule::GetStartAddressHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status, uint16_t address) { if (CheckForRDMError(response, status)) return; JsonSection section; UIntItem *item = new UIntItem("DMX Start Address", address, ADDRESS_FIELD); item->SetMin(0); item->SetMax(DMX_UNIVERSE_SIZE - 1); section.AddItem(item); RespondWithSection(response, section); } /* * Set the DMX start address */ string RDMHttpModule::SetStartAddress(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string dmx_address = request->GetParameter(ADDRESS_FIELD); uint16_t address; if (!StringToUInt16(dmx_address, &address)) { return "Invalid start address"; } string error; m_rdm_api.SetDMXAddress( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, address, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the sensor section. */ string RDMHttpModule::GetSensor(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string hint = request->GetParameter(HINT_KEY); uint8_t sensor_id; if (!StringToUInt8(hint, &sensor_id)) { return "Invalid hint (sensor #)"; } string error; m_rdm_api.GetSensorDefinition( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, sensor_id, NewSingleCallback(this, &RDMHttpModule::SensorDefinitionHandler, response, universe_id, uid, sensor_id), &error); return error; } /** * Handle the response to a sensor definition request. */ void RDMHttpModule::SensorDefinitionHandler( HttpResponse *response, unsigned int universe_id, const UID uid, uint8_t sensor_id, const ola::rdm::ResponseStatus &status, const ola::rdm::SensorDescriptor &definition) { ola::rdm::SensorDescriptor *definition_arg = NULL; if (CheckForRDMSuccess(status)) { definition_arg = new ola::rdm::SensorDescriptor(); *definition_arg = definition; } string error; m_rdm_api.GetSensorValue( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, sensor_id, NewSingleCallback(this, &RDMHttpModule::SensorValueHandler, response, definition_arg), &error); if (!error.empty()) m_server->ServeError(response, BACKEND_DISCONNECTED_ERROR + error); } /** * Handle the response to a sensor value request & build the response. */ void RDMHttpModule::SensorValueHandler( HttpResponse *response, ola::rdm::SensorDescriptor *definition, const ola::rdm::ResponseStatus &status, const ola::rdm::SensorValueDescriptor &value) { if (CheckForRDMError(response, status)) { if (definition) delete definition; return; } JsonSection section; stringstream str; if (definition) { section.AddItem(new StringItem("Description", definition->description)); section.AddItem(new StringItem( "Type", ola::rdm::SensorTypeToString(definition->type))); str << definition->range_min << " - " << definition->range_max << " " << ola::rdm::PrefixToString(definition->prefix) << " " << ola::rdm::UnitToString(definition->unit); section.AddItem(new StringItem("Range", str.str())); str.str(""); str << definition->normal_min << " - " << definition->normal_max << " " << ola::rdm::PrefixToString(definition->prefix) << " " << ola::rdm::UnitToString(definition->unit); section.AddItem(new StringItem("Normal Range", str.str())); if (definition->recorded_value_support && ola::rdm::SENSOR_RECORDED_VALUE) { str.str(""); str << value.recorded << " " << ola::rdm::PrefixToString(definition->prefix) << " " << ola::rdm::UnitToString(definition->unit); section.AddItem(new StringItem("Recorded Value", str.str())); } if (definition->recorded_value_support && ola::rdm::SENSOR_RECORDED_RANGE_VALUES) { str.str(""); str << value.lowest << " - " << value.highest << " " << ola::rdm::PrefixToString(definition->prefix) << " " << ola::rdm::UnitToString(definition->unit); section.AddItem(new StringItem("Min / Max Recorded Values", str.str())); } } str.str(""); str << value.present_value << " " << ola::rdm::PrefixToString(definition->prefix) << " " << ola::rdm::UnitToString(definition->unit); section.AddItem(new StringItem("Present Value", str.str())); if (definition && definition->recorded_value_support) { section.AddItem(new HiddenItem("1", RECORD_SENSOR_FIELD)); } section.SetSaveButton("Record Sensor"); RespondWithSection(response, section); delete definition; } /* * Record a sensor value */ string RDMHttpModule::RecordSensor(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string hint = request->GetParameter(HINT_KEY); uint8_t sensor_id; if (!StringToUInt8(hint, &sensor_id)) { return "Invalid hint (sensor #)"; } string error; m_rdm_api.RecordSensors( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, sensor_id, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the device hours section. */ string RDMHttpModule::GetDeviceHours(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDeviceHours( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUIntHandler, response, string("Device Hours")), &error); return error; (void) request; } /** * Set the device hours */ string RDMHttpModule::SetDeviceHours(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string device_hours = request->GetParameter(GENERIC_UINT_FIELD); uint32_t dev_hours; if (!StringToUInt(device_hours, &dev_hours)) return "Invalid device hours"; string error; m_rdm_api.SetDeviceHours( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, dev_hours, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the lamp hours section. */ string RDMHttpModule::GetLampHours(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetLampHours( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUIntHandler, response, string("Lamp Hours")), &error); return error; (void) request; } /** * Set the lamp hours */ string RDMHttpModule::SetLampHours(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string lamp_hours_str = request->GetParameter(GENERIC_UINT_FIELD); uint32_t lamp_hours; if (!StringToUInt(lamp_hours_str, &lamp_hours)) return "Invalid lamp hours"; string error; m_rdm_api.SetLampHours( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, lamp_hours, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the lamp strikes section */ string RDMHttpModule::GetLampStrikes(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetLampStrikes( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUIntHandler, response, string("Lamp Strikes")), &error); return error; (void) request; } /** * Set the lamp strikes */ string RDMHttpModule::SetLampStrikes(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string lamp_strikes_str = request->GetParameter(GENERIC_UINT_FIELD); uint32_t lamp_strikes; if (!StringToUInt(lamp_strikes_str, &lamp_strikes)) return "Invalid lamp strikes"; string error; m_rdm_api.SetLampStrikes( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, lamp_strikes, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the lamp state section */ string RDMHttpModule::GetLampState(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetLampState( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::LampStateHandler, response), &error); return error; (void) request; } /** * Handle the response to lamp state call and build the response */ void RDMHttpModule::LampStateHandler(HttpResponse *response, const ola::rdm::ResponseStatus &status, uint8_t state) { if (CheckForRDMError(response, status)) return; JsonSection section; SelectItem *item = new SelectItem("Lamp State", GENERIC_UINT_FIELD); typedef struct { string label; ola::rdm::rdm_lamp_state state; } values_s; values_s possible_values[] = { {"Off", ola::rdm::LAMP_OFF}, {"On", ola::rdm::LAMP_ON}, {"Strike", ola::rdm::LAMP_STRIKE}, {"Standby", ola::rdm::LAMP_STANDBY}}; for (unsigned int i = 0; i != sizeof(possible_values) / sizeof(values_s); ++i) { item->AddItem(possible_values[i].label, possible_values[i].state); if (state == possible_values[i].state) item->SetSelectedOffset(i); } section.AddItem(item); RespondWithSection(response, section); } /** * Set the lamp state */ string RDMHttpModule::SetLampState(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string lamp_state_str = request->GetParameter(GENERIC_UINT_FIELD); uint8_t lamp_state; if (!StringToUInt8(lamp_state_str, &lamp_state)) { return "Invalid lamp state"; } string error; m_rdm_api.SetLampState( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, lamp_state, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the lamp mode section */ string RDMHttpModule::GetLampMode(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetLampMode( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::LampModeHandler, response), &error); return error; (void) request; } /** * Handle the response to lamp mode call and build the response */ void RDMHttpModule::LampModeHandler(HttpResponse *response, const ola::rdm::ResponseStatus &status, uint8_t mode) { if (CheckForRDMError(response, status)) return; JsonSection section; SelectItem *item = new SelectItem("Lamp Mode", GENERIC_UINT_FIELD); typedef struct { string label; ola::rdm::rdm_lamp_mode mode; } values_s; values_s possible_values[] = { {"Off", ola::rdm::LAMP_ON_MODE_OFF}, {"DMX", ola::rdm::LAMP_ON_MODE_DMX}, {"On", ola::rdm::LAMP_ON_MODE_ON}, {"On After Calibration", ola::rdm::LAMP_ON_MODE_AFTER_CAL}}; for (unsigned int i = 0; i != sizeof(possible_values) / sizeof(values_s); ++i) { item->AddItem(possible_values[i].label, possible_values[i].mode); if (mode == possible_values[i].mode) item->SetSelectedOffset(i); } section.AddItem(item); RespondWithSection(response, section); } /** * Set the lamp mode */ string RDMHttpModule::SetLampMode(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string lamp_mode_str = request->GetParameter(GENERIC_UINT_FIELD); uint8_t lamp_mode; if (!StringToUInt8(lamp_mode_str, &lamp_mode)) { return "Invalid lamp mode"; } string error; m_rdm_api.SetLampMode( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, lamp_mode, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the device power cycles section */ string RDMHttpModule::GetPowerCycles(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDevicePowerCycles( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUIntHandler, response, string("Device Power Cycles")), &error); return error; (void) request; } /** * Set the device power cycles */ string RDMHttpModule::SetPowerCycles(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string power_cycles_str = request->GetParameter(GENERIC_UINT_FIELD); uint32_t power_cycles; if (!StringToUInt(power_cycles_str, &power_cycles)) return "Invalid power cycles"; string error; m_rdm_api.SetDevicePowerCycles( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, power_cycles, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the display invert section. */ string RDMHttpModule::GetDisplayInvert(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDisplayInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::DisplayInvertHandler, response), &error); return error; } /** * Handle the response to display invert call and build the response */ void RDMHttpModule::DisplayInvertHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status, uint8_t value) { if (CheckForRDMError(response, status)) return; JsonSection section; SelectItem *item = new SelectItem("Display Invert", DISPLAY_INVERT_FIELD); item->AddItem("Off", ola::rdm::DISPLAY_INVERT_OFF); item->AddItem("On", ola::rdm::DISPLAY_INVERT_ON); item->AddItem("Auto", ola::rdm::DISPLAY_INVERT_AUTO); if (value <= ola::rdm::DISPLAY_INVERT_AUTO) item->SetSelectedOffset(value); section.AddItem(item); RespondWithSection(response, section); } /** * Set the display invert. */ string RDMHttpModule::SetDisplayInvert(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string invert_field = request->GetParameter(DISPLAY_INVERT_FIELD); uint8_t display_mode; if (!StringToUInt8(invert_field, &display_mode)) { return "Invalid display mode"; } string error; m_rdm_api.SetDisplayInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, display_mode, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the display level section. */ string RDMHttpModule::GetDisplayLevel(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetDisplayLevel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::DisplayLevelHandler, response), &error); return error; } /** * Handle the response to display level call and build the response */ void RDMHttpModule::DisplayLevelHandler(HttpResponse *response, const ola::rdm::ResponseStatus &status, uint8_t value) { if (CheckForRDMError(response, status)) return; JsonSection section; UIntItem *item = new UIntItem("Display Level", value, GENERIC_UINT_FIELD); item->SetMin(0); item->SetMax(255); section.AddItem(item); RespondWithSection(response, section); } /** * Set the display level. */ string RDMHttpModule::SetDisplayLevel(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string display_level_str = request->GetParameter(GENERIC_UINT_FIELD); uint8_t display_level; if (!StringToUInt8(display_level_str, &display_level)) { return "Invalid display mode"; } string error; m_rdm_api.SetDisplayLevel( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, display_level, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the pan invert section. */ string RDMHttpModule::GetPanInvert(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetPanInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUInt8BoolHandler, response, string("Pan Invert")), &error); return error; } /** * Set the pan invert. */ string RDMHttpModule::SetPanInvert(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string mode = request->GetParameter(GENERIC_BOOL_FIELD); string error; m_rdm_api.SetPanInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, mode == "1", NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the tilt invert section. */ string RDMHttpModule::GetTiltInvert(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetTiltInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUInt8BoolHandler, response, string("Tilt Invert")), &error); return error; } /** * Set the tilt invert. */ string RDMHttpModule::SetTiltInvert(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string mode = request->GetParameter(GENERIC_BOOL_FIELD); string error; m_rdm_api.SetTiltInvert( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, mode == "1", NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the pan/tilt swap section. */ string RDMHttpModule::GetPanTiltSwap(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetPanTiltSwap( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericUInt8BoolHandler, response, string("Pan Tilt Swap")), &error); return error; } /** * Set the pan/tilt swap. */ string RDMHttpModule::SetPanTiltSwap(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string mode = request->GetParameter(GENERIC_BOOL_FIELD); string error; m_rdm_api.SetPanTiltSwap( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, mode == "1", NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the clock section. */ string RDMHttpModule::GetClock(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetClock( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::ClockHandler, response), &error); return error; } /** * Handle the response to clock call and build the response */ void RDMHttpModule::ClockHandler(HttpResponse *response, const ola::rdm::ResponseStatus &status, const ola::rdm::ClockValue &clock) { if (CheckForRDMError(response, status)) return; JsonSection section; stringstream str; str << std::setfill('0') << std::setw(2) << static_cast(clock.hour) << ":" << std::setw(2) << static_cast(clock.minute) << ":" << std::setw(2) << static_cast(clock.second) << " " << static_cast(clock.day) << "/" << static_cast(clock.month) << "/" << clock.year; section.AddItem(new StringItem("Clock", str.str())); RespondWithSection(response, section); } /** * Handle the request for the identify mode section. */ string RDMHttpModule::GetIdentifyMode(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetIdentifyMode( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::GenericBoolHandler, response, string("Identify Mode")), &error); return error; } /* * Set the idenify mode */ string RDMHttpModule::SetIdentifyMode(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string mode = request->GetParameter(GENERIC_BOOL_FIELD); string error; m_rdm_api.IdentifyDevice( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, mode == "1", NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Handle the request for the power state section. */ string RDMHttpModule::GetPowerState(HttpResponse *response, unsigned int universe_id, const UID &uid) { string error; m_rdm_api.GetPowerState( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, NewSingleCallback(this, &RDMHttpModule::PowerStateHandler, response), &error); return error; } /** * Handle the response to power state call and build the response */ void RDMHttpModule::PowerStateHandler(HttpResponse *response, const ola::rdm::ResponseStatus &status, uint8_t value) { if (CheckForRDMError(response, status)) return; JsonSection section; SelectItem *item = new SelectItem("Power State", GENERIC_UINT_FIELD); typedef struct { string label; ola::rdm::rdm_power_state state; } values_s; values_s possible_values[] = { {"Full Off", ola::rdm::POWER_STATE_FULL_OFF}, {"Shutdown", ola::rdm::POWER_STATE_SHUTDOWN}, {"Standby", ola::rdm::POWER_STATE_STANDBY}, {"Normal", ola::rdm::POWER_STATE_NORMAL}}; for (unsigned int i = 0; i != sizeof(possible_values) / sizeof(values_s); ++i) { item->AddItem(possible_values[i].label, possible_values[i].state); if (value == possible_values[i].state) item->SetSelectedOffset(i); } section.AddItem(item); RespondWithSection(response, section); } /* * Set the power state. */ string RDMHttpModule::SetPowerState(const HttpRequest *request, HttpResponse *response, unsigned int universe_id, const UID &uid) { string power_state_str = request->GetParameter(GENERIC_UINT_FIELD); uint8_t power_state; ola::rdm::rdm_power_state power_state_enum; if (!StringToUInt8(power_state_str, &power_state) || !ola::rdm::UIntToPowerState(power_state, &power_state_enum)) { return "Invalid power state"; } string error; m_rdm_api.SetPowerState( universe_id, uid, ola::rdm::ROOT_RDM_DEVICE, power_state_enum, NewSingleCallback(this, &RDMHttpModule::SetHandler, response), &error); return error; } /** * Check if the id url param exists and is valid. */ bool RDMHttpModule::CheckForInvalidId(const HttpRequest *request, unsigned int *universe_id) { string uni_id = request->GetParameter(ID_KEY); if (!StringToUInt(uni_id, universe_id)) { OLA_INFO << "Invalid universe id: " << uni_id; return false; } return true; } /** * Check that the uid url param exists and is valid. */ bool RDMHttpModule::CheckForInvalidUid(const HttpRequest *request, UID **uid) { string uid_string = request->GetParameter(UID_KEY); *uid = UID::FromString(uid_string); if (*uid == NULL) { OLA_INFO << "Invalid uid: " << uid_string; return false; } return true; } /* * Check the response to a Set RDM call and build the response. */ void RDMHttpModule::SetHandler( HttpResponse *response, const ola::rdm::ResponseStatus &status) { string error; CheckForRDMSuccessWithError(status, &error); RespondWithError(response, error); } /* * Build a response to a RDM call that returns a uint32_t */ void RDMHttpModule::GenericUIntHandler(HttpResponse *response, string description, const ola::rdm::ResponseStatus &status, uint32_t value) { if (CheckForRDMError(response, status)) return; JsonSection section; section.AddItem(new UIntItem(description, value, GENERIC_UINT_FIELD)); RespondWithSection(response, section); } /* * Build a response to a RDM call that returns a bool */ void RDMHttpModule::GenericUInt8BoolHandler( HttpResponse *response, string description, const ola::rdm::ResponseStatus &status, uint8_t value) { GenericBoolHandler(response, description, status, value > 0); } /* * Build a response to a RDM call that returns a bool */ void RDMHttpModule::GenericBoolHandler(HttpResponse *response, string description, const ola::rdm::ResponseStatus &status, bool value) { if (CheckForRDMError(response, status)) return; JsonSection section; section.AddItem(new BoolItem(description, value, GENERIC_BOOL_FIELD)); RespondWithSection(response, section); } /** * Check for an RDM error, and if it occurs, return a json response. * @return true if an error occured. */ bool RDMHttpModule::CheckForRDMError(HttpResponse *response, const ola::rdm::ResponseStatus &status) { string error; if (!CheckForRDMSuccessWithError(status, &error)) { RespondWithError(response, error); return true; } return false; } int RDMHttpModule::RespondWithError(HttpResponse *response, const string &error) { response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append("{\"error\": \"" + error + "\"}"); int r = response->Send(); delete response; return r; } /** * Build & send a response from a JsonSection */ void RDMHttpModule::RespondWithSection(HttpResponse *response, const ola::web::JsonSection §ion) { response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append(section.AsString()); response->Send(); delete response; } /* * Check the success of an RDM command * @returns true if this command was ok, false otherwise. */ bool RDMHttpModule::CheckForRDMSuccess( const ola::rdm::ResponseStatus &status) { string error; if (!CheckForRDMSuccessWithError(status, &error)) { OLA_INFO << error; return false; } return true; } /* * Check the success of an RDM command * @returns true if this command was ok, false otherwise. */ bool RDMHttpModule::CheckForRDMSuccessWithError( const ola::rdm::ResponseStatus &status, string *error) { stringstream str; switch (status.ResponseType()) { case ola::rdm::ResponseStatus::TRANSPORT_ERROR: str << "RDM command error: " << status.Error(); if (error) *error = str.str(); return false; case ola::rdm::ResponseStatus::BROADCAST_REQUEST: return false; case ola::rdm::ResponseStatus::REQUEST_NACKED: str << "Request was NACKED with code: " << ola::rdm::NackReasonToString(status.NackReason()); if (error) *error = str.str(); return false; case ola::rdm::ResponseStatus::MALFORMED_RESPONSE: str << "Malformed RDM response " << status.Error(); if (error) *error = str.str(); return false; case ola::rdm::ResponseStatus::VALID_RESPONSE: return true; default: str << "Unknown response status " << static_cast(status.ResponseType()); if (error) *error = str.str(); return false; } } /* * Handle the RDM discovery response * @param response the HttpResponse that is associated with the request. * @param error an error string. */ void RDMHttpModule::HandleBoolResponse(HttpResponse *response, const string &error) { if (!error.empty()) { m_server->ServeError(response, error); return; } response->SetContentType(HttpServer::CONTENT_TYPE_PLAIN); response->Append("ok"); response->Send(); delete response; } /** * Add a section to the supported section list */ void RDMHttpModule::AddSection(vector *sections, const string §ion_id, const string §ion_name, const string &hint) { section_info info = {section_id, section_name, hint}; sections->push_back(info); } } // ola