Функция AutoLisp getPathLoadedApplication или как ее написать ?
| Правила | Регистрация | Пользователи | Поиск | Сообщения за день | Все разделы прочитаны |  Справка по форуму | Файлообменник |

Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > LISP > Функция AutoLisp getPathLoadedApplication или как ее написать ?

Функция AutoLisp getPathLoadedApplication или как ее написать ?

Ответ
Поиск в этой теме
Непрочитано 08.06.2011, 13:15 #1
Функция AutoLisp getPathLoadedApplication или как ее написать ?
alex-63
 
инженер
 
Брест
Регистрация: 15.09.2009
Сообщений: 55

Господа, программирующие на AutoLispe подскажите функцию возвращающую, загруженные приложения (arx dbx lsp vlx fas etc)
аналог команды _appload где можно было бы узнать с какой директории загрузилось приложение. Меня интересует vlx приложение.

alex-63
Просмотров: 9547
 
Непрочитано 08.06.2011, 13:35
#2
hwd

C, C++, C#
 
Регистрация: 07.10.2009
С-Пб.
Сообщений: 2,762
Отправить сообщение для hwd с помощью Skype™


ObjectARX - в командной строке набрать: _arx _c
Lisp - в командной строке набрать: _lsp _c

п.с. правда это будет перечень подгруженных команд, а не имён библиотечных файлов.
__________________
Надеюсь, ты не социальный овощ? Это определяется делами! :welcome:
hwd вне форума  
 
Непрочитано 08.06.2011, 13:47
#3
ытя


 
Регистрация: 23.09.2005
СПб
Сообщений: 428


(vl-list-loaded-vlx) - возвращает список имён загруженных .vlx
ытя вне форума  
 
Автор темы   Непрочитано 08.06.2011, 16:35
#4
alex-63

инженер
 
Регистрация: 15.09.2009
Брест
Сообщений: 55


Меня интересует путь откуда загружено приложение.
Нужна функция похожая на (arx) но чтобы возвращала путь и имя приложения причем не только arx а lsp vlx
(getPathLoadedApplication)->'("c:\ProgramFile\Autodesk\acapp.arx" "c:\etc\test.vlx ... )
alex-63 вне форума  
 
Непрочитано 08.06.2011, 18:16
#5
gomer

строю, ломаю
 
Регистрация: 03.04.2008
Украина
Сообщений: 5,515


Цитата:
Сообщение от alex-63 Посмотреть сообщение
Меня интересует путь откуда загружено приложение.
из лиспа никак
gomer вне форума  
 
Непрочитано 08.06.2011, 18:23
#6
hwd

C, C++, C#
 
Регистрация: 07.10.2009
С-Пб.
Сообщений: 2,762
Отправить сообщение для hwd с помощью Skype™


Я знаю как на .net, получить пути для lisp/net. Насчёт получения путей для vba/arx - может и их удастся, но я не пробовал. Если мне не изменяет память, средствами автолиспа получить такие каталоги не удастся (например функция лиспа вроде как не может определить полное имя файла из которого она вызвана). Т.о. чтобы дать лиспу функционал, запрашиваемый топикстартером - нужно писать обёртку, написанную на objectarx/.net (мне видится решение именно таким).
__________________
Надеюсь, ты не социальный овощ? Это определяется делами! :welcome:
hwd вне форума  
 
Непрочитано 08.06.2011, 18:33
#7
gomer

строю, ломаю
 
Регистрация: 03.04.2008
Украина
Сообщений: 5,515


Цитата:
Сообщение от hwd Посмотреть сообщение
Насчёт получения путей для vba/arx - может и их удастся
Удастся... стоит поискать... уже обсуждалось и не раз
gomer вне форума  
 
Автор темы   Непрочитано 10.06.2011, 09:57
#8
alex-63

инженер
 
Регистрация: 15.09.2009
Брест
Сообщений: 55


Написал сам на c++ используя класс acDocManager из objectArx неужели нет доступа к этому классу из autoLispa ?
Код прилагаю может кому пригодиться т.к считаю она должна быть библиотечной функцией и почему ее до сих пор нет ?
Жаль что autolisp все меньше поддерживают в autodesk.
Код:
[Выделить все]
	static int ads_getloadedapplication(void)
	{
		//----- Remove the following line if you do not expect any argument for this ADS function
		struct resbuf *pArgs =acedGetArgs () ;
		// TODO: add your code here         
		resbuf *prbRet  = NULL;
		resbuf *prbTail = NULL;			
		AcApDocument *doc = acDocManager->curDocument();  
//		acutPrintf(_T("File Name %s"),doc->fileName());
int lispAppCount = doc->GetCountOfLispList();
if(lispAppCount > 0){	
	for(int i =0; i<lispAppCount;i++){
		AcLispAppInfo* appInfo;		
		if(appInfo=doc->GetItemOfLispList(i))
		{
			if(prbTail) { 
				prbTail->rbnext = acutNewRb(RTSTR);
				prbTail=prbTail->rbnext;
			             } else 
			{
				prbTail=acutNewRb(RTSTR);
				prbRet=prbTail;
						 }
						 acutNewString(appInfo->appFileName ,prbTail->resval.rstring);		
                //acutPrintf(_T("\n %s"),appInfo->appFileName);
		}//if
	} //for

if( prbRet )
	{ 	    
		acedRetList( prbRet );		
		acutRelRb( prbRet ); 		

	} 

} else
		// TODO: Replace the following line by your returned value if any
{
		acedRetNil () ;
}

		return (RSRSLT) ;
	}

Последний раз редактировалось Кулик Алексей aka kpblc, 10.06.2011 в 10:33.
alex-63 вне форума  
 
Непрочитано 10.06.2011, 17:04
#9
gomer

строю, ломаю
 
Регистрация: 03.04.2008
Украина
Сообщений: 5,515


Цитата:
Сообщение от alex-63 Посмотреть сообщение
Написал сам на c++ используя класс acDocManager из objectArx неужели нет доступа к этому классу из autoLispa ?
Ну, хорошо, а теперь представьте, что следующая вызванная функция перемещает загруженный лисп или меняет его содержимое
gomer вне форума  
 
Непрочитано 10.06.2011, 19:14
#10
Pastor

это только кличка
 
Регистрация: 22.10.2006
Москва
Сообщений: 252


Цитата:
...аналог команды _appload где можно было бы узнать с какой директории загрузилось приложение. Меня интересует vlx приложение.
Напишите приложение - менеджер-загрузчик других приложений. Он всегда и абсолютно точно будет знать, что и откуда загрузил.
Один из вариантов выкладывал hwd на своем сайте.

Если нужно просто в коде vlx-файла определить, откуда этот vlx загружен, то при условии, что файл расположен по одному из путей поддержки AutoCAD, путь можно узнать (setq fullpath (findfile "ИмяФайла.vlx")).
__________________
...в шее моей жилы железные, и лоб мой - медный...

Последний раз редактировалось Pastor, 10.06.2011 в 19:22.
Pastor вне форума  
 
Непрочитано 11.06.2011, 23:24
#11
Александр Ривилис

программист, рыцарь ObjectARX
 
Регистрация: 09.05.2005
Киев
Сообщений: 2,413
Отправить сообщение для Александр Ривилис с помощью Skype™


Цитата:
Сообщение от Pastor Посмотреть сообщение
Если нужно просто в коде vlx-файла определить, откуда этот vlx загружен, то при условии, что файл расположен по одному из путей поддержки AutoCAD, путь можно узнать (setq fullpath (findfile "ИмяФайла.vlx")).
И при условии, что он загружался без указания полного пути к нему. Если есть два файла с одним именем в путях доступа AutoCAD и загружен по полному пути не тот, который находится при помощи (findfile ...) то путь будет неверным.
Цитата:
Сообщение от alex-63 Посмотреть сообщение
Жаль что autolisp все меньше поддерживают в autodesk.
Как поддерживался, так и поддерживается.

Цитата:
Сообщение от alex-63 Посмотреть сообщение
Код прилагаю может кому пригодиться т.к считаю она должна быть библиотечной функцией и почему ее до сих пор нет ?
Ты кажется не понимаешь природу Autolisp, когда функции могут генерироваться на лету, вообще без использования файлов.
Александр Ривилис вне форума  
 
Непрочитано 11.06.2011, 23:36
#12
gomer

строю, ломаю
 
Регистрация: 03.04.2008
Украина
Сообщений: 5,515


А весь сырбор из-за того, чтоб менюшку подгрузить...
gomer вне форума  
 
Автор темы   Непрочитано 16.06.2011, 16:52
#13
alex-63

инженер
 
Регистрация: 15.09.2009
Брест
Сообщений: 55


Мне иногда надо загрузить autoLisp и иметь доступ к ресурсам(например файлу конфигурации) .
Если проект не указан в пути доступа в AUTOCAD то поиск при помощи функции findfile не возможен.
Бывает что несколько файлов с одинаковым именем расположены по разным директориям и какой файл загружен так же
удобно использовать выше написанную функцию на C++ используя ObjectArx.
Функция (getloadedlispapp) котороя возвращает то же самое что показано в диалоговом окне и команда appload
->("C:\\Program Files\\Autodesk\\AutoCAD 2012 - English\\Express\\acetutil.fas"
2012 - English\\R18.2\\enu\\support\\acad.mnl" "G:\\AutoLisp\\LIB\\ak_lib0.lsp"
"G:\\AutoLisp\\LIB\\ak_max_min.lsp" "G:\\AutoLisp\\LIB\\ak_util.lsp"
"G:\\AutoLisp\\EXCEL\\ak_EXCEL.lsp" "G:\\AutoLisp\\ak_tools.mnl" "C:\\Documents
and Settings\\Пользователь\\Application Data\\Autodesk\\AutoCAD 2012 -
English\\R18.2\\enu\\support\\acetmain.mnl"
"G:\\AutoLisp\\AK_STAIR\\AK_STAIR.VLX")
etc
Например загруженное приложение AK_STAIR.VLX не указано в пути Support File Search и доступ к файлу конфигурации AK_STAIR.ini
получить проблематично не имея function (getLoadedLispApp).

Возможно кто-то и использует autoLISP в ИИ и генерирует функции которые сами пишут функций, но для этого удобнее использовать Common Lisp
используя например defmacro и если вы используете эти библиотеки поделитесь, мне будет интересно.
alex-63 вне форума  
 
Непрочитано 16.06.2011, 17:05
#14
Александр Ривилис

программист, рыцарь ObjectARX
 
Регистрация: 09.05.2005
Киев
Сообщений: 2,413
Отправить сообщение для Александр Ривилис с помощью Skype™


Цитата:
Сообщение от alex-63 Посмотреть сообщение
Бывает что несколько файлов с одинаковым именем расположены по разным директориям и какой файл загружен так же
удобно использовать выше написанную функцию на C++ используя ObjectArx.
А если загружены > 1 файла с одинаковым именем?
P.S.: Аналогичную функцию я написал 6 лет назад (здесь), но она оказалась невостребованной именно по причине "возможных вариантов", когда однозначно нельзя сказать какой это файл.
Александр Ривилис вне форума  
 
Непрочитано 16.06.2011, 17:10
#15
Дима_

Продуман
 
Регистрация: 22.02.2007
Питер
Сообщений: 2,839


Беда в Вашей функции - в том что она под objectarx - ну есть у меня загруженный лисп - как мне загрузить Вашу функцию? - где она? - надо к ней знать путь, если я его знаю - то значит программа проинсталированна или путь "жестко" прописан - а при таком раскладе мне она не нужна - с этими условиями можно и средствами лиспа получить конфигурацию (по тому-же пути). Если только не доходить до крайности - вписывать Ваш бинарный код (в каком нибудь base64) в лисп (под все версии?), распаковывать в какой-нибудь временный файл и запускать (либо-же брать исходный файл - но лучше на .net т.к. компиляторы встроены в дистрибутив, компилировать его на лету (с использованием библиотек текущей системы) и запускать) - но это получается большой гемор - непонятно из-за чего.
__________________
Когда в руках молоток все вокруг кажется гвоздями.
Дима_ вне форума  
 
Непрочитано 16.06.2011, 17:22
#16
Александр Ривилис

программист, рыцарь ObjectARX
 
Регистрация: 09.05.2005
Киев
Сообщений: 2,413
Отправить сообщение для Александр Ривилис с помощью Skype™


Цитата:
Сообщение от Дима_ Посмотреть сообщение
но это получается большой гемор - непонятно из-за чего.
Точнее не понятно ради чего. И потом не забывай, что .NET можно грузить только командой (_NETLOAD), что уже само по себе не хорошо.
Александр Ривилис вне форума  
 
Непрочитано 16.06.2011, 17:40
#17
hwd

C, C++, C#
 
Регистрация: 07.10.2009
С-Пб.
Сообщений: 2,762
Отправить сообщение для hwd с помощью Skype™


Нужно изначально писать код так, чтобы не пришлось под каждую лиспину добавлять очередную запись в каталоги поиска.
Для этого в каталогах поиска задаёшь всего один путь, например C:\AutoCAD\Addons\LISP.
Теперь каждого пакета лиспов создаёшь в этот каталоге свой подкаталог и кидаешь лиспы в него, а в коде этих лиспов поиск ресурсов задаёшь с учётом имени подкаталога в котором они находится.
Т.е. если твой пакет лежит в C:\AutoCAD\Addons\LISP\Sample, то в коде поиск всяких там dcl-файлов, иконок и т.п. нужно прописывать в виде "Sample\\ИмяФайла", а не просто "ИмяФайла", как зачастую пишут некоторые говнокодеры (по другому язык не поворачивается сказать).

Имея общий знаменатель в виде C:\AutoCAD\Addons\LISP (или др. путь - на твоё усмотрение) и обозначая поиск ресурсов с учётом родительского каталога, ты сразу же снимаешь с себя проблему необходимости добавлять под каждый каталог с лиспами свой путь в список поиска, зашлаковывая тем самым профиль автокада и снижая скорость его работы).

А если ещё и имена функциям будешь давать согласованные (начиная со своего префикса), а не такие как dt, pb, cmd1, www и т.п. - то сможешь ещё избежать и конфликтов имён.

Хотя, как показывает практика - гуана с подобным названиями/указаниями ресурсов всё равно будет море в тех лиспах, которые тебе притащат юзеры, понабрав их с двг.ру и кадюзера, ибо каждый из таких кодо-писателей считает себя единственным и неповторимым, и потому не допускает видимо мысли о том, что помимо его лиспа юзер может загрузить и др лиспы и пишет код так, что для того, чтобы его багоподелие работало - юзер должен под него добавить в каталоги поиск очередной путь.

Имхо

Цитата:
Сообщение от Александр Ривилис
Точнее не понятно ради чего. И потом не забывай, что .NET можно грузить только командой (_NETLOAD), что уже само по себе не хорошо.
Здесь посмотрите код класса загрузчика (класс AcadPlaginsLoader, строки кода 287-299) - как видите, не только _NETLOAD...
__________________
Надеюсь, ты не социальный овощ? Это определяется делами! :welcome:
hwd вне форума  
 
Автор темы   Непрочитано 16.06.2011, 18:08
#18
alex-63

инженер
 
Регистрация: 15.09.2009
Брест
Сообщений: 55


Не плохо бы иметь библиотеку OpenLibForAutolisp с исходными текстами на C++ C#Net VBNet VBA типа OpenDCL для AutoLispa
Господин Александр Ривилис написал 6 лет функцию скомпилировал, а исходников не выложил.
Пришлось тратить время. А время деньги ...
Например с подшивками AutoLIsp не дружит и неплохо бы иметь функции доступа к этому объекту ...
alex-63 вне форума  
 
Непрочитано 16.06.2011, 18:27
#19
hwd

C, C++, C#
 
Регистрация: 07.10.2009
С-Пб.
Сообщений: 2,762
Отправить сообщение для hwd с помощью Skype™


Цитата:
Сообщение от alex-63 Посмотреть сообщение
Не плохо бы иметь библиотеку OpenLibForAutolisp с исходными текстами на C++ C#Net VBNet VBA типа OpenDCL для AutoLispa
Господин Александр Ривилис написал 6 лет функцию скомпилировал, а исходников не выложил.
Пришлось тратить время. А время деньги ...
Например с подшивками AutoLIsp не дружит и неплохо бы иметь функции доступа к этому объекту ...
Писать одни и те же исходники отдельно на C#, отдельно на VB.NET - это полное извращение (одного языка достаточно). OpenLibForAutolisp (ещё и не понятно, какой функционал в него предполагает вгонять автор) - во первых писать никто не будет (а если и найдётся энтузиаст - надолго его не хватит); во вторых - зачем изобретать лыжи для лиспа, если это можно сделать на др. языке? Только для того, чтобы облегчить жизнь тем, кто окромя автолиспа др. языков не знает? Ну так это их проблемы, и решать тогда должны они же - пусть изучают языки и пишут на них доп. функционал для лиспа. Однако по факту, если они выучат др. язык, то под лисп писать уже не станут (это логично и верно), ибо смысл коню приделывать коньки, когда можно сразу уже писать на этом выученном языке... Можно конечно написать на дотнете команду, которая из автолиспа, к примеру, позволит создать образ DVD-диска, но только возникает вопрос: "а нахрена?"... Подход "всё_на_свете_делать_с_помощью_автолиспа" - это плохо, у автолиспа должна быть своя песочница, и эта песочница - автокад.

Автодеск положил на это дело болт (не развивает автолисп), хотя существует слабая надежда, что когда-то снова продолжит, т.к. в опросе есть такой пункт, мол на что по вашему следует сделать упор в развитии - там и лисп присутствовал в качестве пункта выбора...

имхо
__________________
Надеюсь, ты не социальный овощ? Это определяется делами! :welcome:
hwd вне форума  
 
Непрочитано 16.06.2011, 18:56
#20
Александр Ривилис

программист, рыцарь ObjectARX
 
Регистрация: 09.05.2005
Киев
Сообщений: 2,413
Отправить сообщение для Александр Ривилис с помощью Skype™


Цитата:
Сообщение от hwd Посмотреть сообщение
Здесь посмотрите код класса загрузчика (класс AcadPlaginsLoader, строки кода 287-299) - как видите, не только _NETLOAD...
Я про загрузку при помощи lisp.

Цитата:
Сообщение от alex-63 Посмотреть сообщение
Господин Александр Ривилис написал 6 лет функцию скомпилировал, а исходников не выложил.
Не куда и некому было выкладывать. Вот так это выглядело тогда:
Код:
[Выделить все]
//-----------------------------------------------------------------------------
//----- acrxEntryPoint.h
//-----------------------------------------------------------------------------
#include "StdAfx.h"
#include "resource.h"

//-----------------------------------------------------------------------------
#define szRDS _RXST("")

//-----------------------------------------------------------------------------
//----- ObjectARX EntryPoint
class CGetLispInfoApp : public AcRxArxApp {

public:
  CGetLispInfoApp () : AcRxArxApp () {}

  virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) {
    // TODO: Load dependencies here

    // You *must* call On_kInitAppMsg here
    AcRx::AppRetCode retCode =AcRxArxApp::On_kInitAppMsg (pkt) ;
    
    // TODO: Add your initialization code here

    return (retCode) ;
  }

  virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) {
    // TODO: Add your code here

    // You *must* call On_kUnloadAppMsg here
    AcRx::AppRetCode retCode =AcRxArxApp::On_kUnloadAppMsg (pkt) ;

    // TODO: Unload dependencies here

    return (retCode) ;
  }

  virtual void RegisterServerComponents () {
  }


  // ----- ads_getlispfilepath symbol (do not rename)
  static int ads_getlispfilepath(void)
  {
    //----- Remove the following line if you do not expect any argument for this ADS function
    struct resbuf *pArgs =acedGetArgs () ;
    // TODO: Replace the following line by your returned value if any
    acedRetNil();
    // TODO: add your code here
    AcApDocument *pDoc = acDocManagerPtr()->curDocument();
    if (pArgs && pArgs->restype == RTSTR) {
      // Считаем, что передали имя lsp-файла
      int nLisp = pDoc->GetCountOfLispList();
      AcLispAppInfo *pLispInfo = NULL;
      for (int i=0; i < nLisp; i++) {
        if (pLispInfo = pDoc->GetItemOfLispList(i)) {
          char path_buffer[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR];
          char fname[_MAX_FNAME], ext[_MAX_EXT];
          _splitpath(pLispInfo->appFileName,drive,dir,fname,ext);
          strcpy(path_buffer,fname); strcat(path_buffer,ext);
          if (!stricmp(path_buffer,pArgs->resval.rstring)) {
            acedRetStr(pLispInfo->appFileName);
            break;
          }
        }
      }

    } else {
      // Нужно вернуть список путей ко всем загруженным lsp-файлам
      int nLisp = pDoc->GetCountOfLispList();
      AcLispAppInfo *pLispInfo = NULL;
      resbuf *rbb = acutBuildList(RTLB,RTNONE), *rb = rbb;
      for (int i=0; i < nLisp; i++) {
        if (pLispInfo = pDoc->GetItemOfLispList(i)) {
          char path_buffer[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR];
          char fname[_MAX_FNAME], ext[_MAX_EXT];
          _splitpath(pLispInfo->appFileName,drive,dir,fname,ext);
          strcpy(path_buffer,fname); strcat(path_buffer,ext);
          while (rb->rbnext) rb = rb->rbnext;
          rb->rbnext = acutBuildList(RTLB,RTSTR,path_buffer,RTSTR,pLispInfo->appFileName,RTLE,RTNONE);
        }
      }
      while (rb->rbnext) rb = rb->rbnext;
      rb->rbnext = acutBuildList(RTLE,RTNONE);
      acedRetList(rbb); acutRelRb(rbb);
    }

    return (RSRSLT) ;
  }

  // ----- ads_getarxfilepath symbol (do not rename)
  static int ads_getarxfilepath(void)
  {
    //----- Remove the following line if you do not expect any argument for this ADS function
    struct resbuf *pArgs =acedGetArgs () ;

    acedRetNil();
    if (pArgs && pArgs->restype == RTSTR) {
      HMODULE h = GetModuleHandle(pArgs->resval.rstring);
      if (h) {
        char buf[_MAX_PATH+1];
        if (GetModuleFileName(h,buf,sizeof(buf)-1)) {
          acedRetStr(buf);
        }
      }
    } else {
      AcDbVoidPtrArray* pApps = reinterpret_cast<AcDbVoidPtrArray*>(acrxLoadedApps());
      if (pApps==NULL) return RSRSLT;
      resbuf *rbb = acutBuildList(RTLB,RTNONE), *rb = rbb;
      for (int i=0;i<pApps->length();i++) {
        const char *arxname = reinterpret_cast<const char*>(pApps->at(i));
        HMODULE h = GetModuleHandle(arxname);
        char buf[_MAX_PATH+1];
        if (h && GetModuleFileName(h,buf,sizeof(buf)-1)) {
          while (rb->rbnext) rb = rb->rbnext;
          rb->rbnext = acutBuildList(RTLB,RTSTR,arxname,RTSTR,buf,RTLE,RTNONE);
        }
      }
      while (rb->rbnext) rb = rb->rbnext;
      rb->rbnext = acutBuildList(RTLE,RTNONE);
      acedRetList(rbb); acutRelRb(rbb);
      for (;pApps->length()>0;) {
        delete reinterpret_cast<char*>(pApps->at(0));
        pApps->removeAt(0);
      }
      delete pApps;
    }
    return (RSRSLT) ;
  }
} ;

//-----------------------------------------------------------------------------
IMPLEMENT_ARX_ENTRYPOINT(CGetLispInfoApp)

ACED_ADSSYMBOL_ENTRY_AUTO(CGetLispInfoApp, getlispfilepath, true)
ACED_ADSSYMBOL_ENTRY_AUTO(CGetLispInfoApp, getarxfilepath, true)
Александр Ривилис вне форума  
Ответ
Вернуться   Форум DWG.RU > Программное обеспечение > Программирование > LISP > Функция AutoLisp getPathLoadedApplication или как ее написать ?

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
DwgRuLispLib: Функция открытия диалога Open\Save As файла (файлов) Supermax Библиотека функций 20 14.03.2018 10:05
написать программу-листинг в AutoLisp students LISP 31 23.05.2011 23:47
Autolisp функция как аргумент другой функции xsakabsx LISP 9 17.12.2010 14:15
структурированный список Holon Программирование 22 11.09.2007 14:09
Написать текст (autolisp) Torero LISP 2 23.10.2004 23:53