On 08/29/2010 06:42 AM, Phil Dibowitz wrote: > On 08/29/2010 06:18 AM, Stephen Warren wrote: >> Sorry for top-posting, but I'm making a general response rather than to >> individual points. > > You can avoid top-posting in such a situation by just not including the > original email at all. :)
:-) I guess I was thinking of keeping context, but that's what mailing list archives are fo. >> However, I'd prefer a single unified API. > > I agree. > >> 1) Identify/parse any update/... file >> 1a) A function to load/parse/... the file >> 1b) Function(s) to query any information about the parsed file (e.g. >> what type of operation is being performed, so this information can be >> presented to the user) >> 1c) Function(s) to query the number and type of steps required to >> implement the operation. > > I like a and b. As for c, I think it depends on the rest of the API. If we > really abstract all config updates into one (or atleast one consistent set > of) functions, c isn't needed. > >> 2) A single function to perform the entire operation (or perhaps a >> single function per type of operation) > > And given this, I see no need for 1-c above. congruity creates a user-interface with a text label and progress bar for each step. When performing the steps, the progress bars get updated in the callbacks from libconcord. Currently, libconcord hard-codes the set of progress bars to create, but I'm hoping that if libconcord defines the set of required steps, then congruity can simply query that set from libconcord instead of hard-coding the user-interface setup code. That querying needs to happen before executing the steps, so the UI is fully populated before beginning slow programming operations. >> The callback from step 2 would need to be enhanced to include a step ID >> as well as percentage or byte-count, in order to match with the data >> returned by function(s) in 1c above. > > Agreed. Would the step ID be a string, or an ID? Would you want a function > to return a string version (the way the lc_error stuff currently works), or > would that be left up to the client? I think the step ID itself would just be an integer 0..n. Other functions could map from step ID to step type, step name, etc. Those functions would basically be (1c) above. >> I propose this also because I see that some of the operations have XML >> files that can (and do) list multiple "regions" to be updated. Thus, the >> set of operations to be executed is not only driven by remote >> architecture, but also by update/... file content. > > Yes, the 1000 and later all have multiple regions. In fact, the current code > has a not-nice hack where the region is always hard-coded as 4 because > that's how the 89x remotes work. I'll have to fix that once I start working > on the 1000. > >> enum OperationType >> Connectivity >> UpdateConfiguration >> UpdateFirmware >> LearnIR >> >> enum StepType >> InitialWebPing >> PrepareForUpdate >> EraseRegion >> WriteRegion >> VerifyRegion >> FinalizeUpdate >> Reset >> ReconnectToRemote >> SetTime // e.g. yes for 880 no for 700 > > I don't understand what you mean by yes or no. Do you just mean we'd never > return that on the 700? Can the 700 not have it's time set? Yes, "set time" on the 700 appears to be a complete no-op; the command is accepted, but doesn't seem to do anything (get time always returns the current time unmodified) Note that the remote itself never displays the time as far as I can tell, so I guess it makes sense the clock commands do nothing. > FWIW, the 89x never gets rebooted, so I don't _need_ to set the time after > the configuration update, but it seems like a good idea. > > Which actually leads me to a more interesting question about your API > design... is setting the time *really* a setp in UpdateConfiguration? Well, the list above was just a rough idea, and probably isn't complete either. > You can update the configuration without touching the time and you can > update the time without ever updating the configuration. It's > certainly true that I want to support "just fix the time on my remote" > for example. I thought on the 880, the reboot was needed to pick up all the new configuration? If so, I think that should probably be part of an update-configuration step list. Equally, isn't a set-time needed after a reboot, since it get's lost? If so, that operation should be included in the update-configuration step list. >> FinalWebPing >> ... >> >> enum StepStatus >> Starting >> Executing >> Complete_Success >> Failure >> >> Status Callback(ParsedOperationFile *pof, void *cbcontext, >> uint32 step, StepStatus step_status, >> uint complete_count, uint target_count) > > And target_type which would be an enum of Percent, Kb, much like we have > "is_bytes" or whatever it is now, but more extensible. > >> ParsedOperationFile *load_file(char *filename) > > We already have this. It would just need a bit of tweaking to combined > read_file() and determine_file_type() and give it a nice struct. I had > started working on something like it when I started doing the zipfile stuff > for the 89x, but decided to not do too much API surgery until we'd talked > about it a bit. Sure. I just mentioned this explicitly to emphasize that I see it returning a new struct instead of the current "find the binary data pointer" API. > Rather than various functions such as pof_type() I had envisioned returning > a well-defined struct the user could read. I had no planned on exposing > things like "how many regions" and stuff because that should get abstracted > away. I'm find with the accessor functions though. > >> // e.g. region ID for erase/write/verify, which can be added >> // to (or interpolated into printf-style) step labels in the UI >> ??? pof_step_parameters(ParsedOperationFile *pof, uint step, >> // ???: >> enum parameter_type, uint parameter_id) >> >> // or update_config_execute, update_firmware_execute, ...? >> Status pof_execute(ParsedOperationFile *pof, Callback *cb, >> void *context) > > I'm not sure I follow you here. You're expecting something like: > > for (int step = 0; step< pof_num_steps(pof): i++) { > type = pof_step_type(pof, step); > // somehow figure out paramenters > pof_execute(pof, step, ...) > } The application would call a single pof_execute function (or one function per operation type, not per step). The implementation of whatever function the application calls might look like the code you wrote above. > ? I dislike this. I'd rather keep it a bit more similar to what we have, > with say: > > pof_type = pof_type(pof) > switch (pof_type) { > case ConfigUpdate: > UpdateConfiguration(pof, cb, cb_data); > ResetRemote(...); //does nothing if not supported > SetTime(...); > break; > case FirmwareUpdate: > UpdateFirmware(pof, cb, cb_data); > ResetRemote(...); > SetTime(...); > break; > } I'm not completely sure if your objection was to (a) where to iterate over the step list (i.e. what I replied to above), or (b) using the same top-level function for each operation type. Either one pof_execute for anything *or* one separate UpdateConfiguration/UpdateFirmware/... function for each operation type is fine by me. For a completely generic application, having a single top-level function that does anything might be easier, but congruity can handle either just fine. > It's worth noting here that Congruity only provides the functionality of > *part* of the libconcord API. The ability to backup your config, set time, > backup your firmware, etc. is valuable to many of our users. Such users are > generally happy to use the CLI and that's awesome, and I want to continue > supporting such uses cases at the library level. Also, I don't want the API > for cases were no operations file exists to be significantly different from > those were we do. I guess I don't see anything too fundamentally wrong with having a high-level API for typical operations (which I think does include the other stuff you mentioned) and a separate low-level API for more "internal" operations. That said, keeping things as similar as possible makes sense. Certain operations I see being always simple enough they can be available as a direct function call; set_time, reset_remote being great examples. Pretty much any other operation seems like it could be a multi-step process; even "back up firmware to a binary file" that currently reads a single chunk of flash and returns it might be a multi-step read process if there were multiple firmware regions that needed to be read. I could easily imagine separate text/rodata/data stored non-contiguously in different regions. Or firmware backups needing to back up both a bootloader and application. So, perhaps: // Returns TLOConnectivity, TLOUpdateConfiguration, TLOUpdateFirmware TopLevelOperation *gen_op_from_website_file(remote, filename); // Always returns TLOBackupFirwmare TopLevelOperation *gen_backup_firmware_op(remote, filename)\ // ... TopLevelOperation *gen_restore_firmware_op(remote, filename) TopLevelOperation *gen_backup_configuration_op(remote, filename) TopLevelOperation *gen_restore_configuration_op(remote, filename) // or one function per operation type: Status tlo_execute(tlo, callback, ...) (In the case of separate functions per operation type, the TopLevelOperation class would mainly act as a vehicle for the metadata query APIs which allow applications to know what steps get executed.) The alternative to all those gen_* functions would be for the application to iterate over the steps like in the code above, but perhaps calling more specific functions: for (int step = 0; step < pof_num_steps(pof): i++) { step_type = pof_step_type(pof, step); switch (step_type) { case ERASE_REGION: region_address = pof_get_parameter(pof, step, ADDRESS); region_size = pof_get_parameter(pof, step, SIZE); lc_erase_region(remote, region_address, region_size); break; case WRITE_REGION: write_address = pof_get_parameter(pof, step, ADDRESS); write_size = pof_get_parameter(pof, step, SIZE); data_ptr = pof_get_parameter(pof, step, DATA_PTR); lc_write_region(remote, write_address, write_size, data_ptr); break; case RESET_REMOTE: lc_reset_remote(remote); break; //... } } But even with that, I think libconcord should probably define the set of steps required for even "simple" things like config backup, in case the steps become more complex. > Anyway, then the callback system could be updated in much the way you > describe. > > Which is all basically "option 1" from my email, but you flushed out the > "and we need to fix the callback system", plus I like your suggestion of > extending our API around the operations files. > >> The callback could return Continue/Abort to allow implementation of a >> cancel button in a UI. > > In *theory* this would be really cool, but in reality this will almost > always leave the remote in a very bad state, and so I think we should avoid > this. In practice, users can press CTRL-C while running any application, or click the cancel button in congruity (which will abort between libconcord API calls right now), or just unplug their remote. In this case, the worst that should happen is the remote booting into safe mode and needing another firmware or configuration update attempt. ------------------------------------------------------------------------------ This SF.net Dev2Dev email is sponsored by: Show off your parallel programming skills. Enter the Intel(R) Threading Challenge 2010. http://p.sf.net/sfu/intel-thread-sfd _______________________________________________ concordance-devel mailing list concordance-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/concordance-devel