{"action":"created","issue":{"url":"https://api.github.com/repos/RasppleII/a2cloud/issues/15","labels_url":"https://api.github.com/repos/RasppleII/a2cloud/issues/15/labels{/name}","comments_url":"https://api.github.com/repos/RasppleII/a2cloud/issues/15/comments","events_url":"https://api.github.com/repos/RasppleII/a2cloud/issues/15/events","html_url":"https://github.com/RasppleII/a2cloud/issues/15","id":117655078,"number":15,"title":"For jessie: Trying to understand adtpro.sh","user":{"login":"iKarith","id":10780557,"avatar_url":"https://avatars.githubusercontent.com/u/10780557?v=3","gravatar_id":"","url":"https://api.github.com/users/iKarith","html_url":"https://github.com/iKarith","followers_url":"https://api.github.com/users/iKarith/followers","following_url":"https://api.github.com/users/iKarith/following{/other_user}","gists_url":"https://api.github.com/users/iKarith/gists{/gist_id}","starred_url":"https://api.github.com/users/iKarith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iKarith/subscriptions","organizations_url":"https://api.github.com/users/iKarith/orgs","repos_url":"https://api.github.com/users/iKarith/repos","events_url":"https://api.github.com/users/iKarith/events{/privacy}","received_events_url":"https://api.github.com/users/iKarith/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/RasppleII/a2cloud/labels/documentation","name":"documentation","color":"fbca04"},{"url":"https://api.github.com/repos/RasppleII/a2cloud/labels/question","name":"question","color":"cc317c"}],"state":"open","locked":false,"assignee":{"login":"IvanExpert","id":1761075,"avatar_url":"https://avatars.githubusercontent.com/u/1761075?v=3","gravatar_id":"","url":"https://api.github.com/users/IvanExpert","html_url":"https://github.com/IvanExpert","followers_url":"https://api.github.com/users/IvanExpert/followers","following_url":"https://api.github.com/users/IvanExpert/following{/other_user}","gists_url":"https://api.github.com/users/IvanExpert/gists{/gist_id}","starred_url":"https://api.github.com/users/IvanExpert/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/IvanExpert/subscriptions","organizations_url":"https://api.github.com/users/IvanExpert/orgs","repos_url":"https://api.github.com/users/IvanExpert/repos","events_url":"https://api.github.com/users/IvanExpert/events{/privacy}","received_events_url":"https://api.github.com/users/IvanExpert/received_events","type":"User","site_admin":false},"milestone":null,"comments":2,"created_at":"2015-11-18T18:54:38Z","updated_at":"2015-11-19T23:59:18Z","closed_at":null,"body":"Hey Ivan, I think I mostly have the adtpro.sh script figured out, but maybe you can provide some of your thinking in a few places.  This script and the udev rules are basically why it doesn't work with systemd, but I think I have a handle on how to do that.  Mostly.  Anyway, let's have a look at the diff:\r\n\r\n```diff\r\n--- ../../adtpro/build/adtprobase.sh\t2015-11-02 07:36:07.000000000 -0800\r\n+++ adtpro.sh.txt\t2015-10-30 05:47:22.000000000 -0700\r\n@@ -1,5 +1,6 @@\r\n-#!/bin/sh\r\n-#\r\n+#! /bin/bash\r\n+# vim: set tabstop=4 shiftwidth=4 expandtab filetype=sh:\r\n+\r\n # ADTPro - *nix startup shell script\r\n #\r\n # Note:\r\n```\r\n\r\nThat's totally on me.  :)  I actually didn't check the scripts for bashisms, I just made them all use bash since most of the scripts did.  Might wanna revert that for this script if it truly isn't necessary.  Anyway…\r\n\r\n```diff\r\n@@ -12,9 +13,9 @@\r\n #\r\n # Set default ADTPRO_HOME to be the fully qualified\r\n # current working directory.\r\n-export ADTPRO_HOME=\"`dirname \\\"$0\\\"`\"\r\n-cd \"$ADTPRO_HOME\"\r\n-export ADTPRO_HOME=`pwd`\r\n+#export ADTPRO_HOME=\"`dirname \\\"$0\\\"`\"\r\n+#cd \"$ADTPRO_HOME\"\r\n+#export ADTPRO_HOME=`pwd`\r\n \r\n # Uncomment and modify one or both of the lines below if you\r\n # want to specify a particular location for Java or ADTPro.\r\n@@ -24,21 +25,74 @@\r\n # export MY_JAVA_HOME=/usr/local/java/bin/\r\n # export ADTPRO_HOME=~/myuser/adtpro\r\n \r\n+usageExit () {\r\n+    echo \"usage:\" 1>&2\r\n+    echo \"adtpro.sh [headless] [serial|ethernet|audio|localhost] [serialPortName]\" 1>&2\r\n+    exit 1\r\n+}\r\n+\r\n+export ADTPRO_HOME=/usr/local/adtpro\r\n+cd \"$ADTPRO_HOME\"\r\n+\r\n OS=`uname`\r\n-OS_ARCH=`uname -p`\r\n+OS_ARCH=`uname -m`\r\n+\r\n+[[ $1 == \"headless\" ]] && { headless=1; shift; } || headless=\r\n+\r\n+if [[ $1 && ( $1 != \"serial\" && $1 != \"ethernet\" && $1 != \"audio\" && $1 != \"localhost\" ) ]]; then\r\n+    usageExit\r\n+fi\r\n\r\n```\r\n\r\nSetting ADTPRO_HOME is obvious.  Changing uname -p to `uname -m` is less obvious, until you note that uname -p returns unknown on many Linux installations, including the Raspberry Pi.  I see you changed how headless is processed here—entirely so you could add the usage message?  If so, I'll make the usage message work more the way the rest of the script does and submit that change to David for upstream inclusion. \r\n\r\nThe real meat follows:\r\n\r\n```diff \r\n # For Linux, use this:\r\n if [ \"$OS\" = \"Linux\" ]; then\r\n+\r\n+    serialPortName=\r\n+    if [[ $1 == \"serial\" ]]; then\r\n+        if [[ $2 ]]; then\r\n+            serialPortName=\"$2\"\r\n+            [[ ${serialPortName:0:5} == \"/dev/\" ]] && serialPortName=${serialPortName:5}\r\n+            if [[ ! -c /dev/$serialPortName ]]; then\r\n+                echo \"Serial port $serialPortName not found.\" 1>&2\r\n+                usageExit\r\n+            fi\r\n+        elif [[ -c /dev/ttyUSBlower ]]; then\r\n+            serialPortName=ttyUSBlower\r\n+        elif [[ $(ls -1 /dev/ttyUSBlower_hub* 2> /dev/null | wc -l) -gt 0 ]]; then\r\n+            serialPortName=$(ls -1 /dev/ttyUSBlower_hub* 2> /dev/null | head -1 | cut -c 6-)\r\n+        elif [[ $(ls -1 /dev/ttyUSBupper_hub* 2> /dev/null | wc -l) -gt 1 ]]; then\r\n+            serialPortName=$(ls -1 /dev/ttyUSBupper_hub* 2> /dev/null | head -1 | cut -c 6-)\r\n+        else\r\n+            echo \"No eligible USB-to-serial adapter found.\" 1>&2\r\n+            echo \"Possible ports:\" 1>&2\r\n+            echo \" lower USB port\"\r\n+            echo \" any port on lower USB hub with no other adapters\"\r\n+            echo \" lowest port on USB hub on upper or lower USB port with multiple adapters\"\r\n+            usageExit\r\n+        fi\r\n+    fi\r\n+\r\n+    if [[ $(grep CommPort= /usr/local/adtpro/disks/ADTPro.properties) ]]; then\r\n+        if [[ $serialPortName ]]; then\r\n+            sed -i \"s/^CommPort=.*$/CommPort=\\/dev\\/$serialPortName/\" /usr/local/adtpro/disks/ADTPro.properties &> /dev/null\r\n+        else\r\n+            serialPortName=$(grep 'CommPort=/dev/' ADTPro.properties 2> /dev/null | cut -f 3 -d '/')\r\n+        fi\r\n+    else\r\n+        echo -e \"#ADTPro.properties\\n#$(date)\\nCommPortSpeed=115200\\nCommPortBootstrapSpeed=2400\\nCommPort=/dev/$serialPortName\\nCommPortBootstrapPacing=250\\nHardwareHandshaking=false\\nSerialIPHost=localhost\\nSerialIPPort=1977\" > /usr/local/adtpro/disks/ADTPro.properties\r\n+        chmod ugo+w /usr/local/adtpro/disks/ADTPro.properties\r\n+    fi\r\n+\r\n+    ADTPRO_EXTRA_JAVA_PARMS=\"-Dgnu.io.rxtx.SerialPorts=/dev/$serialPortName\"\r\n+\r\n   if [ -f /usr/bin/raspi-config ]; then\r\n-    export RXTXLIB=lib/rxtx/%RXTX_VERSION%/arm\r\n-    ADTPRO_EXTRA_JAVA_PARMS=\"-Dgnu.io.rxtx.SerialPorts=/dev/ttyUSB0:/dev/ttyAMA0\"\r\n+        export RXTXLIB=lib/rxtx/rxtx-2.2pre2-local/arm\r\n   elif [ \"$OS_ARCH\" = \"i686\" ]; then\r\n-    export RXTXLIB=lib/rxtx/%RXTX_VERSION%/i686-pc-linux-gnu\r\n+        export RXTXLIB=lib/rxtx/rxtx-2.2pre2-local/i686-pc-linux-gnu\r\n   else\r\n     if [ \"$OS_ARCH\" = \"i386\" ]; then\r\n-      export RXTXLIB=lib/rxtx/%RXTX_VERSION%/i686-pc-linux-gnu\r\n+            export RXTXLIB=lib/rxtx/rxtx-2.2pre2-local/i686-pc-linux-gnu\r\n     else  \r\n-      export RXTXLIB=lib/rxtx/%RXTX_VERSION%/x86_64-unknown-linux-gnu\r\n+            export RXTXLIB=lib/rxtx/rxtx-2.2pre2-local/x86_64-unknown-linux-gnu\r\n     fi\r\n   fi\r\n fi\r\n```\r\n\r\nThat's a big bite.  There's a fair number of lines we can ignore due to the minus side being a script template.  I note you try to force ONE serial port to be used here, and we've already discussed how the UDEV rules you've got now just won't quite do going forward.  But I also think upstream does it wrong here by hard-coding ttyUSB0 and ttyAMA0 on the Pi.  Probably the best solution all around is either a UDEV rule that does what /dev/serial SHOULD do (enumerate all serial devices, not just the USB ones), or to write some function that can build a list of serial devices.  The trick there is that we'd probably prefer /dev/serial/by-path/ entries over /dev/tty* entries for those that have them.\r\n\r\nIs there a reason you create the ADTPro properties file here instead of just running it? with serial and the port name to use in the environment?\r\n\r\nLet's skip the OS X and Solaris stuff since that's just template related.\r\n\r\n\r\n```diff\r\n-if [ \"$1x\" = \"headlessx\" ]; then\r\n-  shift\r\n-  if [ \"$1x\" = \"x\" ] || [ ! -f /usr/bin/xvfb-run ]; then\r\n-    if [ ! -f /usr/bin/xvfb-run ]; then\r\n+if [[ $headless ]]; then\r\n+    if [[ ! $1 || ! -f /usr/bin/xvfb-run ]]; then\r\n+        if [[ ! -f /usr/bin/xvfb-run ]]; then\r\n       echo \"Headless operation requires xvfb.\"\r\n+            usageExit\r\n     else\r\n-      echo \"usage: adtpro.sh [ headless ] [ serial | ethernet | audio | localhost ]\"\r\n+            echo \"Headless operation requires a communication mode (e.g. serial).\"\r\n+            usageExit\r\n     fi\r\n     exit 1\r\n   else\r\n```\r\n\r\nThere's the rest of the headless and other argument parsing getting moved around.  I'd probably change those `[[ ! -f /usr/bin/xvfb-run ]]`'s to `! command -v xvfb-run >/dev/null`.  That's another one for upstream too.\r\n\r\n\r\n```bash\r\n@@ -75,5 +130,13 @@\r\n   fi\r\n fi\r\n \r\n+if [[ $serialPortName && $(ps aux | grep \"/sbin/getty.*$serialPortName\") ]]; then\r\n+    sudo pkill -f \"/sbin/getty.*$serialPortName\"\r\n+fi\r\n+sudo pkill -f [A]DTPro\r\n cd \"$ADTPRO_HOME\"/disks\r\n-$HEADLESS\"$MY_JAVA_HOME\"java -Xms256m -Xmx512m \"$TWEAK\" $ADTPRO_EXTRA_JAVA_PARMS -cp ../lib/%ADTPRO_VERSION%:../\"$RXTXLIB\"/../RXTXcomm.jar:../lib/AppleCommander/AppleCommander-%AC_VERSION%.jar org.adtpro.ADTPro $*\r\n\\ No newline at end of file\r\n+$HEADLESS\"$MY_JAVA_HOME\"java -Xms256m -Xmx512m \"$TWEAK\" $ADTPRO_EXTRA_JAVA_PARMS -cp ../lib/ADTPro.jar:../\"$RXTXLIB\"/../RXTXcomm.jar:../lib/AppleCommander/AppleCommander-ac.jar org.adtpro.ADTPro $* &\r\n+if [[ $1 == \"serial\" ]]; then\r\n+    echo \"Starting up on interface $serialPortName. Please wait...\"\r\n+fi\r\n+sleep 30\r\n```\r\n\r\nWhat was the reason for the sleep 30 in there?  I'm betting that's the reason why my Pi's boot process seems to halt an annoyingly long time vs. vanilla Raspbian.  ;)\r\n\r\nThat bit there with disabling a getty that might be running on the serial port in question probably isn't real systemd compatible.  Likely the ttyusbhandler also breaks it.  That'll want to be a systemd service file most likely.  Fortunately, it's actually pretty easy to write those so that you can configure what port causes stuff to happen and what to have happen, and make the whole thing contingent on there being enough system running to actually have the thing start.\r\n\r\nAnyway, if there's anything you can add to all of that, lemme know!\r\n\r\nThanks!  :)"},"comment":{"url":"https://api.github.com/repos/RasppleII/a2cloud/issues/comments/158238228","html_url":"https://github.com/RasppleII/a2cloud/issues/15#issuecomment-158238228","issue_url":"https://api.github.com/repos/RasppleII/a2cloud/issues/15","id":158238228,"user":{"login":"iKarith","id":10780557,"avatar_url":"https://avatars.githubusercontent.com/u/10780557?v=3","gravatar_id":"","url":"https://api.github.com/users/iKarith","html_url":"https://github.com/iKarith","followers_url":"https://api.github.com/users/iKarith/followers","following_url":"https://api.github.com/users/iKarith/following{/other_user}","gists_url":"https://api.github.com/users/iKarith/gists{/gist_id}","starred_url":"https://api.github.com/users/iKarith/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iKarith/subscriptions","organizations_url":"https://api.github.com/users/iKarith/orgs","repos_url":"https://api.github.com/users/iKarith/repos","events_url":"https://api.github.com/users/iKarith/events{/privacy}","received_events_url":"https://api.github.com/users/iKarith/received_events","type":"User","site_admin":false},"created_at":"2015-11-19T23:59:18Z","updated_at":"2015-11-19T23:59:18Z","body":"On Thu, Nov 19, 2015 at 03:57:50AM -0800, IvanExpert wrote:\n>> That's totally on me. :) I actually didn't check the scripts for bashisms, I just made them all use bash since most of the scripts did. Might wanna revert that for this script if it truly isn't necessary. Anyway…\n>>\n>Your call. Can't remember anything about that.\n\nI'll give it a once over at least.  Nothing there looks to be a \nbashism and diff reduction is a good idea in genera.\n\n\n\n>>  I see you changed how headless is processed here—entirely so you could add the usage message?  If so, I'll make the usage message work more the way the rest of the script does and submit that change to David for upstream inclusion.\n>So, this is interesting -- there was no headless capability at all in stock ADTPro in version 1.2.7, which is what I based the script on, only a single parameter to specifiy starting up in a particular communication mode. It looks like after the release of A2CLOUD, Dave must have liked the headless capability and put a simplified version into the 1.2.9 startup script. So you could say that it's Dave who changed how headless is processed, although it may be that he simply copied however I did things in a much earlier iteration of A2CLOUD. (And, I do like clear usage messages.)\n\nAh interesting!  Well then, I think I will re-implement the clean \nusage instructions on top of his version and submit that as a patch \nupstream.  Good to know that he's watching what we do for the Pi and \ntaking ideas from it.\n\n\n>> That's a big bite. There's a fair number of lines we can ignore due to the minus side being a script template. I note you try to force ONE serial port to be used here, and we've already discussed how the UDEV rules you've got now just won't quite do going forward.\n>>\n>Why won't UDEV rules do again, at leastl for Raspberry Pi? I don't want to throw the baby out with the bathwater, unless a better solution can be proposed.\n\nYou can use udev rules, but the idea under systemd is that udev \ntriggers systemd which recognizes a service should be started for a \ngiven port, and starts it.\n\nI may actually write udev rules to create a better /dev/serial.  \nTheoretically /dev/serial exists to do what you did with the USBlower \nand USBupper.  My irritation with it is that it ONLY contains USB \nserial devices!  Mine looks like this:\n\n/dev/serial\n|-- by-id\n|   |-- usb-FTDI_USB_Serial_Converter_FT8ZQX5V-if00-port0 -> ../../ttyUSB3\n|   |-- usb-FTDI_USB_Serial_Converter_FTGUK9F7-if00-port0 -> ../../ttyUSB0\n|   |-- usb-Keyspan__a_division_of_InnoSys_Inc._Keyspan_USB_Serial_Adapter-if00-port0 -> ../../ttyUSB1\n|   `-- usb-Keyspan__a_division_of_InnoSys_Inc._Keyspan_USB_Serial_Adapter-if00-port1 -> ../../ttyUSB2\n`-- by-path\n    |-- platform-3f980000.usb-usb-0:1.2:1.0-port0 -> ../../ttyUSB3\n    |-- platform-3f980000.usb-usb-0:1.4:1.0-port0 -> ../../ttyUSB0\n    |-- platform-3f980000.usb-usb-0:1.5:1.0-port0 -> ../../ttyUSB1\n    `-- platform-3f980000.usb-usb-0:1.5:1.0-port1 -> ../../ttyUSB2\n\n2 directories, 8 files\n\n\nIn fact that tree illustrates the problem--ttyUSB3 is going to be \nttyUSB0 on my next reboot most likely.  But it doesn't matter if I \nhad referred to it using either the by-id or by-path symlink.\n\nNotably the two FTDI serial devices have some kind of serial number \nthing there.  I don't know where that comes from, but the absolutely \nridiculous Keyspan__a_divisiion_of_Stupid_Device_Naming_Inc. (which \nis unsupported by default, see #14 for that), does not have one.\n\nI think a device name like /dev/tty-serial/usb-0:1.2:1.0-port0 or \n/dev/tty-serial/arm-gpio-uart-port0 or /dev/tty-serial/pc-port0 would \nbe quite descriptive.  The USB numbering will be arbitrary to an end \nuser, but all they need to know is that once they identify it, that \nwill be its name as long as they don't go rearranging their USB \nconnections.\n\n\n>> But I also think upstream does it wrong here by hard-coding ttyUSB0 and ttyAMA0 on the Pi.\n>>\n>I'd agree. It looks like Dave also put that into the 1.2.9 script, based on an early version of A2CLOUD. That was how I used to do things before implementing the UDEV aliases. Early A2CLOUD was virtual drives only, no serial comm or Apple II Pi, so I figured one of those two would get the job done.\n\nGood to know that Dave got it from you.  That means if I can come up \nwith something that's better, we can give him that instead.  :)\n\n\n>> Probably the best solution all around is either a UDEV rule that does what /dev/serial SHOULD do (enumerate all serial devices, not just the USB ones), or to write some function that can build a list of serial devices. The trick there is that we'd probably prefer /dev/serial/by-path/ entries over /dev/tty* entries for those that have them.\n>>\n>The unsolveable problem, until you prove me otherwise, is that if you've got more than one USB-to-serial of the same chipset, as might be the case in a typical A2CLOUD setup, there is neither a) any way of distinguishing them from one another, nor b) predicting what names they're going to get in /dev/ if they're both present at startup. Hardcoding for actual known hardware ports, even if it's only for a specific hardware platform, is a better user experience, which is why I made the UDEV rules.\n\nUnfortunately you can't.  You need some way to identify the device by \nwhere it is connected and possibly by its manufacturer.  I don't \ndispute that in the least.  But trying to physically describe the USB \nport layout on three major versions of the Raspberry Pi, the five \nversions of Banana Pi, the five ODROID boards, ARM-based ChromeOS \ndevices, and anything else that one might install Raspbian on ...\n\nBasically Raspbian has become to armv6hf/armv7hf devices what Debian \nis to pretty much everything else.  It's not that creating symlinks \nto ports using names that won't change is a bad idea.  It's just that \nwe need more generic names, and to let the user tell us which one \nhe's connecting where.  I have four USB-serial devices above.  I'm \nnot likely to bother with ttyAMA0, for example.\n\nI think the solution to that is some udev rules like the above along \nwith a config script that guides you through the process of assigning \nyour serial ports however you want them.\n\n\n>Also, ttyAMA0 is reserved for Apple II Pi, since that's what the Apple II Pi card uses, so I don't consider it any more; and it requires a boutique cable to pull up the 3.3v on the Pi to 5v.\n\nBut you might not have an Apple II Pi card.  If you didn't get one \nbefore, you can't currently get one now.  A level shifter can be had \non eBay cheaply enough, but it's kinda fiddly to connect.  If you're \nusing an actual serial port (SSC or port), you'd be just as happy \nusing another USB-serial device.  It's less fiddly that way.\n\nI'd rather not tie anyone into anything, though I agree ttyAMA0 is \nthe logical default for those who have the hardware.  It just should \nbe possible to change it if you know the default is the wrong choice \nin your case.\n\n\n>> Is there a reason you create the ADTPro properties file here instead of just running it? with serial and the port name to use in the environment?\n>>\n>Absolutely, but I can't remember exactly why. I think it's for the variable-specified serial port name off the bat. If you just run it, I think you get a default serial port, or no serial port, or you can't start up in the serial comm mode at the wanted comm rate, or something, and in any event something didn't work. It may also have been to provide the local IP capability in an early version of A2CLOUD. In other words, I remember *something* not working if I didn't do it this way, but I can't remember what.\n\nI can bet that I can guess what it is now:\n\nYou generally start it headless when the device is connected.  But.  \nIf you weren't starting it headless, if a port had not yet been \nconfigured, the user would be expected to pick a port, verify the \nspeed and handshaking, etc, and then start listening on serial.\n\nIf this is the first time you've run ADTPro, it hasn't got those \ndefaults saved yet.  So how can you start it running headless to use \nserial port settings that haven't been selected yet?\n\nI have a feeling at some point I'm producing a shell script that \nduplicates the preference settings dialogs.  Actually, if I could \nalso pass command line arguments to do bootstrapping, and could start \nkeeping track of the PID ... hmm.  Some of this will definitely \nrequire patching the java program a bit, but you might never need to \nactually see the ADTPro interface.  That's good if you actually don't \nhave a display connected to it.\n\n\n>> +sleep 30\n>> What was the reason for the sleep 30 in there? I'm betting that's the reason why my Pi's boot process seems to halt an annoyingly long time vs. vanilla Raspbian. ;)\n>>\n>So, I don't like it, but there's a reason for it, which unfortunately I'm also hazy about. Again, something didn't work well without it. It either has to do with making sure a new disk image is properly loaded, or flushed, or something; but I think it's because Java runs so slowly on the ARMv6 Pi's that it was a way of making sure that ADTPro server was actually running before the user tried to do anything. With the GUI you'd wait until you see the server window, and then wait some more to see the \"serial\" button become highlighted; but headless you can't do that, so this is the workaround to making sure it's initialized.\n>\n>As for your startup delay, maybe, but I thought I worked around that so it could start up in the background. Maybe I didn't.\n\nI'll just make it sleep only if stdout is a tty.\n\n\n>> That bit there with disabling a getty that might be running on the serial port in question probably isn't real systemd compatible. Likely the ttyusbhandler also breaks it.\n>>\n>Surely.\n>> That'll want to be a systemd service file most likely. Fortunately, it's actually pretty easy to write those so that you can configure what port causes stuff to happen and what to have happen, and make the whole thing contingent on there being enough system running to actually have the thing start.\n>>\n>That's all you, man. Make it so!\n\nI'll see what I can come up with.\n\nJoseph\n\n"}}