<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    Mark,<br>
    <br>
    I'll have a look.<br>
    <br>
    Regards,<br>
    Michael<br>
    <br>
    <br>
    <div class="moz-cite-prefix">Am 01.09.20 um 07:30 schrieb Mark
      Webb-Johnson:<br>
    </div>
    <blockquote type="cite"
      cite="mid:9393E6BF-E1C9-495C-88CE-16082D7678A0@webb-johnson.net">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      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 class="" color="#000000">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 class="" color="#000000">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
          class="" color="#000000"> </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=""
                moz-do-not-send="true">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" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev" moz-do-not-send="true">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=""
                moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a><br
                class="">
              <a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a><br
                class="">
            </div>
          </blockquote>
        </div>
        <br class="">
      </div>
      <br>
      <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>
    <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26</pre>
  </body>
</html>