<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Michael,<div class=""><br class=""></div><div class="">Still struggling with this. It seems like your DuktapeObject will do this, but I can’t work out how it works.</div><div class=""><br class=""></div><div class="">Here are some notes one what I have done so far:</div><div class=""><br class=""></div><div class=""><ol class="MailOutline"><li class="">Created a stub DuktapeConsoleCommand (derived from DuktapeObject) in ovms_duktape.{h,cpp}. This should hold enough to be able to call the javascript callback method for that object. It also stores the module filename (so the registration can be removed when the module is unloaded).<br class=""><br class=""></li><li class="">Provide a DuktapeCommandMap m_cmdmap in ovms_duktape.{h,cpp} OvmsDuktape class that stores a mapping from OvmsCommand to DuktapeConsoleCommand.<br class=""><br class=""></li><li class="">Created a OvmsDuktape::RegisterDuktapeConsoleCommand in ovms_duktape.{h,cpp) that (a) creates the OvmsCommand() object, (b) registers it, (c) creates the DuktapeConsoleCommand() object, and (d) updates a map from OvmsCommand->DuktapeConsoleCommand. There Is also a single callback DukOvmsCommandRegisterRun designed to be run by all.<br class=""><br class=""></li><li class="">Created hooks NotifyDuktapeModuleLoad, NotifyDuktapeModuleUnload, and NotifyDuktapeModuleUnloadAll in OvmsDuktape. The javascript module is identified by filename (path to module or script on vfs, usually, but may also be an internal module). The Unload functions look through the m_cmdmap and unregister commands for javascript modules being unloaded.<br class=""><br class=""></li><li class="">Provide an implementation for ovms_command DukOvmsCommandRegister to support registering commands from Javascript modules. This should extract the details, and then call OvmsDuktape::RegisterDuktapeConsoleCommand to do the actual registration. This has been implemented, except for the callback method (and somehow passing that method from Javascript in the OvmsCommand.Register javascript call).<br class=""><br class=""></li><li class="">Provide a stub implementation for <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">DukOvmsCommandRegisterRun. This uses m_cmdmap to lookup the </span><font color="#000000" class="">DuktapeConsoleCommand object for the command to be run. It should execute the callback method (but that part is not yet implemented).</font></li></ol></div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">I still need help with #5 and #6. What needs to be implemented in DuktapeConsoleCommand, and how is the parameter in <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">OvmsCommand.Register used to store the callback (#5)? Then how to callback the command method from</span> <span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">DukOvmsCommandRegisterRun</span><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class=""> (#6)? If you have time, it is probably much quicker for you to simply make those changes.</span></div><div class=""><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class=""><br class=""></span></div><div class=""><font color="#000000" class="">An alternative implementation would be to do something like the pubsub framework, where the mapping command->callback is done from within a javascript module. That I could do, but it seems your </font><span style="caret-color: rgb(0, 0, 0); color: rgb(0, 0, 0);" class="">DuktapeObject can do it better.</span><font color="#000000" class=""> </font></div><div class=""><br class=""></div><div class="">Thanks, Mark.<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On 15 Jul 2020, at 3:34 PM, Michael Balzer <<a href="mailto:dexter@expeedo.de" class="">dexter@expeedo.de</a>> wrote:</div><br class="Apple-interchange-newline"><div class="">
  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
  
  <div class="">
    Mark,<br class="">
    <br class="">
    yes, I needed that persistence for the HTTP and VFS classes, but I
    also needed to be able to couple a dynamic C++ instance with a JS
    object and have a mechanism to prevent garbage collection while the
    C++ side is still in use. If the C++ side is no longer needed, the
    JS finalizer also needs to imply the C++ instance can be deleted.<br class="">
    <br class="">
    That is all implemented by DuktapeObject. DuktapeObject also
    provides JS method invocation on the coupled JS object and a mutex
    for concurrency protection.<br class="">
    <br class="">
    We probably need some more framework documentation than the header
    comments (applies to all of our framework classes…):<br class="">
    <br class="">
    <tt class="">/***************************************************************************************************</tt><tt class=""><br class="">
    </tt><tt class=""> * DuktapeObject: coupled C++ / JS object</tt><tt class=""><br class="">
    </tt><tt class=""> * </tt><tt class=""><br class="">
    </tt><tt class=""> *  Intended for API methods to attach internal API state
      to a JS object and provide</tt><tt class=""><br class="">
    </tt><tt class=""> *    a standard callback invocation interface for JS
      objects in local scopes.</tt><tt class=""><br class="">
    </tt><tt class=""> *  </tt><tt class=""><br class="">
    </tt><tt class=""> *  - Override CallMethod() to implement specific method
      calls</tt><tt class=""><br class="">
    </tt><tt class=""> *  - Override Finalize() for specific destruction in JS
      context (garbage collection)</tt><tt class=""><br class="">
    </tt><tt class=""> *  - call Register() to prevent normal garbage collection
      (but not heap destruction)</tt><tt class=""><br class="">
    </tt><tt class=""> *  - call Ref() to protect against deletion (reference
      count)</tt><tt class=""><br class="">
    </tt><tt class=""> *  - call Lock() to protect concurrent access (recursive
      mutex)</tt><tt class=""><br class="">
    </tt><tt class=""> *  </tt><tt class=""><br class="">
    </tt><tt class=""> *  - GetInstance() retrieves the DuktapeObject associated
      with a JS object if any</tt><tt class=""><br class="">
    </tt><tt class=""> *  - Push() pushes the JS object onto the Duktape stack</tt><tt class=""><br class="">
    </tt><tt class=""> *  </tt><tt class=""><br class="">
    </tt><tt class=""> *  Note: the DuktapeObject may persist after the JS object
      has been finalized, e.g.</tt><tt class=""><br class="">
    </tt><tt class=""> *    if some callbacks are pending after the Duktape heap
      has been destroyed.</tt><tt class=""><br class="">
    </tt><tt class=""> *    Use IsCoupled() to check if the JS object is still
      available.</tt><tt class=""><br class="">
    </tt><tt class=""> *  </tt><tt class=""><br class="">
    </tt><tt class=""> *  Ref/Unref:</tt><tt class=""><br class="">
    </tt><tt class=""> *    Normal life cycle is from construction to
      finalization. Pending callbacks extend</tt><tt class=""><br class="">
    </tt><tt class=""> *    the life until the last callback has been processed.
      A subclass may extend the life</tt><tt class=""><br class="">
    </tt><tt class=""> *    by calling Ref(), which increases the reference
      count. Unref() deletes the instance</tt><tt class=""><br class="">
    </tt><tt class=""> *    if no references are left.</tt><tt class=""><br class="">
    </tt><tt class=""> */</tt><tt class=""><br class="">
    </tt><br class="">
    You normally just need to use Register/Deregister & Ref/Unref,
    and to implement the constructor and CallMethod. Coupling of the
    instances normally is done on construction, as a JS object is
    normally already needed for the parameters and can simply be
    attached to.<br class="">
    <br class="">
    Have a look at DuktapeHTTPRequest, DuktapeVFSLoad and
    DuktapeVFSSave, these are the current subclasses using this.<br class="">
    <br class="">
    For the command registration I would probably couple the OvmsCommand
    instance with a JS command object providing an execution method.<br class="">
    <br class="">
    Tell me if you need more info.<br class="">
    <br class="">
    Regards,<br class="">
    Michael<br class="">
    <br class="">
    <br class="">
    <div class="moz-cite-prefix">Am 15.07.20 um 08:12 schrieb Mark
      Webb-Johnson:<br class="">
    </div>
    <blockquote type="cite" cite="mid:B291C8AF-8382-4F45-87E3-3DA555E068BE@webb-johnson.net" class="">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
      <div class=""><br class="">
      </div>
      <div class="">@Michael this is probably for you.</div>
      <div class=""><br class="">
      </div>
      <div class="">I am trying to implement javascript command
        registration. The idea is that a javascript module can call
        something like:</div>
      <div class=""><br class="">
      </div>
      <div class="">
        <blockquote style="caret-color: rgb(0, 0, 0); margin: 0px 0px 0px 40px; border: none; padding: 0px;" class="">OvmsCommand.Register(basecommand, name, title,
          callbackfn, usage, min, max)</blockquote>
        <div style="caret-color: rgb(0, 0, 0);" class=""><br class="">
        </div>
      </div>
      <div style="caret-color: rgb(0, 0, 0);" class="">Then we reflect that into MyCommandApp.RegisterCommand,
        and keep a track of which command is for which javascript
        callbackfn. When the command is executed, we pass it into
        duktape.</div>
      <div style="caret-color: rgb(0, 0, 0);" class=""><br class="">
      </div>
      <div style="caret-color: rgb(0, 0, 0);" class="">I also have tracking for javascript module loading and
        unloading, so I can DeregisterCommand() if duktape is reloaded
        (and also protected against commands being registered in
        short-lived scripts run from the command line).</div>
      <div style="caret-color: rgb(0, 0, 0);" class=""><br class="">
      </div>
      <div style="caret-color: rgb(0, 0, 0);" class="">To implement this, I need to store the callbackfn as a
        persistent reference to a duktape javascript function.</div>
      <div class=""><br class="">
      </div>
      <div class="">The issue with callback function references in
        duktape is summarised here:</div>
      <div class=""><br class="">
      </div>
      <blockquote style="margin: 0 0 0 40px; border: none; padding:
        0px;" class="">
        <div class=""><a href="https://wiki.duktape.org/howtonativepersistentreferences" class="" moz-do-not-send="true">https://wiki.duktape.org/howtonativepersistentreferences</a></div>
        <div class=""><br class="">
        </div>
        <div class=""><i class="">When a Duktape/C function is called,
            Duktape places the call arguments on the value stack. While
            the arguments are on the value stack, they're guaranteed to
            be reachable and the Duktape/C function can safely work with
            the arguments.<br class="">
            <br class="">
            However, when the Duktape/C function returns, the value
            stack is unwound and references in the function's value
            stack frame are lost. If the last reference to a particular
            value was in the function's value stack frame, the value
            will be garbage collected when the function return is
            processed.</i></div>
      </blockquote>
      <div class=""><br class="">
      </div>
      <div class="">The standard approach is to store the reference back
        in the duktape duk_push_global_stash so it won’t get
        garbage-collected. But, that seems messy.</div>
      <div class=""><br class="">
      </div>
      <div class="">I see that Michael has already implemented something
        that seems similar in ovms_script.{h, cpp}, for the async http
        callbacks. Presumably to avoid this issue. But, the approach
        seems very different, and I am not sure if it is stopping _all_
        garbage collection for the duration of the async query, or just
        that particular object being garbage collected. The work seems
        extensive (quite a few objects involved).</div>
      <div class=""><br class="">
      </div>
      <div class="">So @Michael, any suggestions for this? I don’t want
        to reinvent the wheel...</div>
      <div class=""><br class="">
      </div>
      <div class="">Regards, Mark.</div>
      <br class="">
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
OvmsDev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:OvmsDev@lists.openvehicles.com">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a>
</pre>
    </blockquote>
    <br class="">
    <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
</pre>
  </div>

_______________________________________________<br class="">OvmsDev mailing list<br class=""><a href="mailto:OvmsDev@lists.openvehicles.com" class="">OvmsDev@lists.openvehicles.com</a><br class="">http://lists.openvehicles.com/mailman/listinfo/ovmsdev<br class=""></div></blockquote></div><br class=""></div></body></html>